diff options
author | Josh Boyer <jwboyer@fedoraproject.org> | 2015-07-08 10:30:06 -0400 |
---|---|---|
committer | Josh Boyer <jwboyer@fedoraproject.org> | 2015-07-08 10:30:06 -0400 |
commit | 8be443055ee741775545f3d86cbfd36410c6ef8d (patch) | |
tree | 90c6fda25ceb55bee60a1d8a30f436eb3b9e1277 | |
parent | 84bb446543a6e771a1be4fc09b0712607bc6a376 (diff) | |
download | kernel-8be443055ee741775545f3d86cbfd36410c6ef8d.tar.gz kernel-8be443055ee741775545f3d86cbfd36410c6ef8d.tar.xz kernel-8be443055ee741775545f3d86cbfd36410c6ef8d.zip |
Linux v4.2-rc1-33-gd6ac4ffc61ac
81 files changed, 40410 insertions, 2 deletions
diff --git a/Documentation-kdbus-Fix-description-of-KDBUS_SEND_SY.patch b/Documentation-kdbus-Fix-description-of-KDBUS_SEND_SY.patch new file mode 100644 index 000000000..8f61e3b4d --- /dev/null +++ b/Documentation-kdbus-Fix-description-of-KDBUS_SEND_SY.patch @@ -0,0 +1,27 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Thu, 9 Apr 2015 13:08:05 +0300 +Subject: [PATCH] Documentation: kdbus: Fix description of + KDBUS_SEND_SYNC_REPLY flag + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Acked-by: Daniel Mack <daniel@zonque.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + Documentation/kdbus/kdbus.message.xml | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/Documentation/kdbus/kdbus.message.xml b/Documentation/kdbus/kdbus.message.xml +index 5e7c7a3f537e..90f6596dcc20 100644 +--- a/Documentation/kdbus/kdbus.message.xml ++++ b/Documentation/kdbus/kdbus.message.xml +@@ -242,8 +242,8 @@ struct kdbus_cmd_send { + </citerefentry>. + + The offset of the reply message in the sender's pool is stored +- in in <varname>offset_reply</varname> when the ioctl has +- returned without error. Hence, there is no need for another ++ in <varname>reply</varname> when the ioctl has returned without ++ error. Hence, there is no need for another + <constant>KDBUS_CMD_RECV</constant> ioctl or anything else to + receive the reply. + </para> diff --git a/Documentation-kdbus-Fix-list-of-KDBUS_CMD_ENDPOINT_U.patch b/Documentation-kdbus-Fix-list-of-KDBUS_CMD_ENDPOINT_U.patch new file mode 100644 index 000000000..df5f12f6c --- /dev/null +++ b/Documentation-kdbus-Fix-list-of-KDBUS_CMD_ENDPOINT_U.patch @@ -0,0 +1,32 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Thu, 9 Apr 2015 13:08:07 +0300 +Subject: [PATCH] Documentation: kdbus: Fix list of KDBUS_CMD_ENDPOINT_UPDATE + errors + +Remove EEXIST. + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Acked-by: Daniel Mack <daniel@zonque.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + Documentation/kdbus/kdbus.endpoint.xml | 7 ------- + 1 file changed, 7 deletions(-) + +diff --git a/Documentation/kdbus/kdbus.endpoint.xml b/Documentation/kdbus/kdbus.endpoint.xml +index 76e325d4e931..c36aa9781739 100644 +--- a/Documentation/kdbus/kdbus.endpoint.xml ++++ b/Documentation/kdbus/kdbus.endpoint.xml +@@ -369,13 +369,6 @@ struct kdbus_cmd { + <constant>KDBUS_ITEM_POLICY_ACCESS</constant> was provided. + </para></listitem> + </varlistentry> +- +- <varlistentry> +- <term><constant>EEXIST</constant></term> +- <listitem><para> +- An endpoint of that name already exists. +- </para></listitem> +- </varlistentry> + </variablelist> + </refsect2> + </refsect1> diff --git a/Documentation-kdbus-Fix-typos.patch b/Documentation-kdbus-Fix-typos.patch new file mode 100644 index 000000000..dcff9f915 --- /dev/null +++ b/Documentation-kdbus-Fix-typos.patch @@ -0,0 +1,279 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Thu, 9 Apr 2015 13:08:04 +0300 +Subject: [PATCH] Documentation: kdbus: Fix typos + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Acked-by: Daniel Mack <daniel@zonque.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + Documentation/kdbus/kdbus.bus.xml | 9 ++++----- + Documentation/kdbus/kdbus.connection.xml | 10 ++++------ + Documentation/kdbus/kdbus.endpoint.xml | 2 +- + Documentation/kdbus/kdbus.item.xml | 9 ++++----- + Documentation/kdbus/kdbus.match.xml | 14 ++++++++------ + Documentation/kdbus/kdbus.message.xml | 11 +++++------ + Documentation/kdbus/kdbus.xml | 6 +++--- + 7 files changed, 29 insertions(+), 32 deletions(-) + +diff --git a/Documentation/kdbus/kdbus.bus.xml b/Documentation/kdbus/kdbus.bus.xml +index 4d875e59ac02..4b9a0ac1b351 100644 +--- a/Documentation/kdbus/kdbus.bus.xml ++++ b/Documentation/kdbus/kdbus.bus.xml +@@ -28,8 +28,7 @@ + <citerefentry> + <refentrytitle>kdbus.message</refentrytitle> + <manvolnum>7</manvolnum> +- </citerefentry> +- ). ++ </citerefentry>). + Each bus is independent, and operations on the bus will not have any + effect on other buses. A bus is a management entity that controls the + addresses of its connections, their policies and message transactions +@@ -42,7 +41,7 @@ + <refentrytitle>kdbus.fs</refentrytitle> + <manvolnum>7</manvolnum> + </citerefentry> +- , a bus is presented as a directory. No operations can be performed on ++ a bus is presented as a directory. No operations can be performed on + the bus itself; instead you need to perform the operations on an endpoint + associated with the bus. Endpoints are accessible as files underneath the + bus directory. A default endpoint called <constant>bus</constant> is +@@ -165,8 +164,8 @@ struct kdbus_cmd { + <citerefentry> + <refentrytitle>kdbus.item</refentrytitle> + <manvolnum>7</manvolnum> +- </citerefentry> +- ) are expected for <constant>KDBUS_CMD_BUS_MAKE</constant>. ++ </citerefentry>) ++ are expected for <constant>KDBUS_CMD_BUS_MAKE</constant>. + </para> + <variablelist> + <varlistentry> +diff --git a/Documentation/kdbus/kdbus.connection.xml b/Documentation/kdbus/kdbus.connection.xml +index 09852125b2d4..cefb419f1093 100644 +--- a/Documentation/kdbus/kdbus.connection.xml ++++ b/Documentation/kdbus/kdbus.connection.xml +@@ -50,8 +50,7 @@ + <citerefentry> + <refentrytitle>kdbus.match</refentrytitle> + <manvolnum>7</manvolnum> +- </citerefentry> +- ). ++ </citerefentry>). + </para> + <para> + Messages synthesized and sent directly by the kernel will carry the +@@ -595,13 +594,13 @@ struct kdbus_cmd_info { + </varlistentry> + + <varlistentry> +- <term><varname>flags</varname></term> ++ <term><varname>attach_flags</varname></term> + <listitem><para> + Specifies which metadata items should be attached to the answer. See + <citerefentry> + <refentrytitle>kdbus.message</refentrytitle> + <manvolnum>7</manvolnum> +- </citerefentry> ++ </citerefentry>. + </para></listitem> + </varlistentry> + +@@ -986,8 +985,7 @@ struct kdbus_cmd { + <term><varname>items</varname></term> + <listitem> + <para> +- Items to describe the connection details to be updated. The +- following item types are supported. ++ The following item types are supported. + </para> + <variablelist> + <varlistentry> +diff --git a/Documentation/kdbus/kdbus.endpoint.xml b/Documentation/kdbus/kdbus.endpoint.xml +index c36aa9781739..6632485f3e84 100644 +--- a/Documentation/kdbus/kdbus.endpoint.xml ++++ b/Documentation/kdbus/kdbus.endpoint.xml +@@ -201,7 +201,7 @@ struct kdbus_cmd { + <para> + To update an existing endpoint, the + <constant>KDBUS_CMD_ENDPOINT_UPDATE</constant> command is used on the file +- descriptor that was used to create the update, using ++ descriptor that was used to create the endpoint, using + <constant>KDBUS_CMD_ENDPOINT_MAKE</constant>. The only relevant detail of + the endpoint that can be updated is the policy. When the command is + employed, the policy of the endpoint is <emphasis>replaced</emphasis> +diff --git a/Documentation/kdbus/kdbus.item.xml b/Documentation/kdbus/kdbus.item.xml +index bfe47362097f..09f8b903116f 100644 +--- a/Documentation/kdbus/kdbus.item.xml ++++ b/Documentation/kdbus/kdbus.item.xml +@@ -139,7 +139,7 @@ struct kdbus_item { + <term><constant>KDBUS_ITEM_NEGOTIATE</constant></term> + <listitem><para> + With this item is attached to any ioctl, programs can +- <emphasis>probe</emphasis> the kernel for known item items. ++ <emphasis>probe</emphasis> the kernel for known item types. + The item carries an array of <type>uint64_t</type> values in + <varname>item.data64</varname>, each set to an item type to + probe. The kernel will reset each member of this array that is +@@ -232,7 +232,6 @@ struct kdbus_memfd { + When received as item attached to a message, the array will + contain the numbers of the installed file descriptors, or + <constant>-1</constant> in case an error occurred. +- file descriptor. + In either case, the number of entries in the array is derived from + the item's total size. See + <citerefentry> +@@ -487,7 +486,7 @@ struct kdbus_pids { + a remote peer is a member of, stored as array of + <type>uint32_t</type> values in <varname>item.data32</varname>. + The array length can be determined by looking at the item's total +- size, subtracting the size of the header and and dividing the ++ size, subtracting the size of the header and dividing the + remainder by <constant>sizeof(uint32_t)</constant>. + </para></listitem> + </varlistentry> +@@ -748,7 +747,7 @@ struct kdbus_notify_name_change { + This item is sent as attachment to a + <emphasis>kernel notification</emphasis>. It informs the receiver + that an expected reply to a message was not received in time. +- The remote peer ID and the message cookie is stored in the message ++ The remote peer ID and the message cookie are stored in the message + header. See + <citerefentry> + <refentrytitle>kdbus.message</refentrytitle> +@@ -765,7 +764,7 @@ struct kdbus_notify_name_change { + <emphasis>kernel notification</emphasis>. It informs the receiver + that a remote connection a reply is expected from was disconnected + before that reply was sent. The remote peer ID and the message +- cookie is stored in the message header. See ++ cookie are stored in the message header. See + <citerefentry> + <refentrytitle>kdbus.message</refentrytitle> + <manvolnum>7</manvolnum> +diff --git a/Documentation/kdbus/kdbus.match.xml b/Documentation/kdbus/kdbus.match.xml +index ef77b64e5890..ae38e04ab4d6 100644 +--- a/Documentation/kdbus/kdbus.match.xml ++++ b/Documentation/kdbus/kdbus.match.xml +@@ -55,7 +55,7 @@ + possibly along with some other rules to further limit the match. + + The kernel will match the signal message's bloom filter against the +- connections bloom mask (simply by &-ing it), and will decide whether ++ connection's bloom mask (simply by &-ing it), and will decide whether + the message should be delivered to a connection. + </para> + <para> +@@ -138,9 +138,9 @@ + <title>Generations</title> + + <para> +- Uploaded matches may contain multiple masks, which have are as large as +- the bloom size defined by the bus. Each block of a mask is called a +- <emphasis>generation</emphasis>, starting at index 0. ++ Uploaded matches may contain multiple masks, which have to be as large ++ as the bloom filter size defined by the bus. Each block of a mask is ++ called a <emphasis>generation</emphasis>, starting at index 0. + + At match time, when a signal is about to be delivered, a bloom mask + generation is passed, which denotes which of the bloom masks the filter +@@ -171,7 +171,8 @@ + <title>Adding a match</title> + <para> + To add a match, the <constant>KDBUS_CMD_MATCH_ADD</constant> ioctl is +- used, which takes a struct of the struct described below. ++ used, which takes a <type>struct kdbus_cmd_match</type> as an argument ++ described below. + + Note that each of the items attached to this command will internally + create one match <emphasis>rule</emphasis>, and the collection of them, +@@ -266,7 +267,8 @@ struct kdbus_cmd_match { + 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 the section below for more information on bloom filters. ++ See the "Bloom filters" section above for more information on ++ bloom filters. + </para> + </listitem> + </varlistentry> +diff --git a/Documentation/kdbus/kdbus.message.xml b/Documentation/kdbus/kdbus.message.xml +index 90f6596dcc20..0115d9d50db3 100644 +--- a/Documentation/kdbus/kdbus.message.xml ++++ b/Documentation/kdbus/kdbus.message.xml +@@ -344,8 +344,7 @@ struct kdbus_cmd_send { + </variablelist> + + <para> +- The fields in this struct are described below. +- The message referenced the <varname>msg_address</varname> above has ++ The message referenced by the <varname>msg_address</varname> above has + the following layout. + </para> + +@@ -528,7 +527,7 @@ struct kdbus_msg { + <listitem> + <para> + Actual data records containing the payload. See section +- "Passing of Payload Data". ++ "Message payload". + </para> + </listitem> + </varlistentry> +@@ -707,7 +706,7 @@ struct kdbus_cmd_recv { + <listitem><para> + Whenever a message with <constant>KDBUS_MSG_SIGNAL</constant> is sent + but cannot be queued on a peer (e.g., as it contains FDs but the peer +- does not support FDs, or there is no space left in the peer's pool..) ++ does not support FDs, or there is no space left in the peer's pool) + the 'dropped_msgs' counter of the peer is incremented. On the next + RECV ioctl, the 'dropped_msgs' field is copied into the ioctl struct + and cleared on the peer. If it was non-zero, the +@@ -963,7 +962,7 @@ struct kdbus_msg_info { + <varlistentry> + <term><constant>E2BIG</constant></term> + <listitem><para> +- Too many items ++ Too many items. + </para></listitem> + </varlistentry> + +@@ -1172,7 +1171,7 @@ struct kdbus_msg_info { + <varlistentry> + <term><constant>EAGAIN</constant></term> + <listitem><para> +- No message found in the queue ++ No message found in the queue. + </para></listitem> + </varlistentry> + </variablelist> +diff --git a/Documentation/kdbus/kdbus.xml b/Documentation/kdbus/kdbus.xml +index 194abd2e76cc..d8e7400df2af 100644 +--- a/Documentation/kdbus/kdbus.xml ++++ b/Documentation/kdbus/kdbus.xml +@@ -379,7 +379,7 @@ + <listitem> + <para> + When a message is sent (<constant>KDBUS_CMD_SEND</constant>), +- information about the sending task and the sending connection are ++ information about the sending task and the sending connection is + collected. This metadata will be attached to the message when it + arrives in the receiver's pool. If the connection sending the + message installed faked credentials (see +@@ -514,7 +514,7 @@ + To let the kernel know which metadata information to attach as items + to the aforementioned commands, it uses a bitmask. In those, the + following <emphasis>attach flags</emphasis> are currently supported. +- Both the the <varname>attach_flags_recv</varname> and ++ Both the <varname>attach_flags_recv</varname> and + <varname>attach_flags_send</varname> fields of + <type>struct kdbus_cmd_hello</type>, as well as the payload of the + <constant>KDBUS_ITEM_ATTACH_FLAGS_SEND</constant> and +@@ -924,7 +924,7 @@ + + <para> + These ioctls, along with the structs they transport, are explained in +- detail in the other documents linked to in the 'see also' section below. ++ detail in the other documents linked to in the "See Also" section below. + </para> + </refsect1> + diff --git a/Documentation-kdbus-Update-list-of-ioctls-which-caus.patch b/Documentation-kdbus-Update-list-of-ioctls-which-caus.patch new file mode 100644 index 000000000..dd2b9825c --- /dev/null +++ b/Documentation-kdbus-Update-list-of-ioctls-which-caus.patch @@ -0,0 +1,31 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Thu, 9 Apr 2015 13:08:06 +0300 +Subject: [PATCH] Documentation: kdbus: Update list of ioctls which cause + writing to receiver's pool + +Add KDBUS_CMD_BUS_CREATOR_INFO. + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Acked-by: Daniel Mack <daniel@zonque.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + Documentation/kdbus/kdbus.pool.xml | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/Documentation/kdbus/kdbus.pool.xml b/Documentation/kdbus/kdbus.pool.xml +index 05fd01902ad4..a9e16f196d39 100644 +--- a/Documentation/kdbus/kdbus.pool.xml ++++ b/Documentation/kdbus/kdbus.pool.xml +@@ -66,6 +66,12 @@ + ... to retrieve information on a connection + </para></listitem> + </varlistentry> ++ <varlistentry> ++ <term><constant>KDBUS_CMD_BUS_CREATOR_INFO</constant></term> ++ <listitem><para> ++ ... to retrieve information about a connection's bus creator ++ </para></listitem> ++ </varlistentry> + </variablelist> + + </para> diff --git a/Documentation-kdbus-fix-location-for-generated-files.patch b/Documentation-kdbus-fix-location-for-generated-files.patch new file mode 100644 index 000000000..66cddfa8e --- /dev/null +++ b/Documentation-kdbus-fix-location-for-generated-files.patch @@ -0,0 +1,41 @@ +From: Daniel Mack <daniel@zonque.org> +Date: Mon, 9 Mar 2015 18:00:46 +0100 +Subject: [PATCH] Documentation: kdbus: fix location for generated files + +The generated files should reside in Documentation/kdbus, not in the +top-level of the source tree. Also add a .gitignore file and ignore +everything that was built from the XML files. + +Signed-off-by: Daniel Mack <daniel@zonque.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + Documentation/kdbus/.gitignore | 2 ++ + Documentation/kdbus/Makefile | 4 ++-- + 2 files changed, 4 insertions(+), 2 deletions(-) + create mode 100644 Documentation/kdbus/.gitignore + +diff --git a/Documentation/kdbus/.gitignore b/Documentation/kdbus/.gitignore +new file mode 100644 +index 000000000000..b4a77ccba9b4 +--- /dev/null ++++ b/Documentation/kdbus/.gitignore +@@ -0,0 +1,2 @@ ++*.7 ++*.html +diff --git a/Documentation/kdbus/Makefile b/Documentation/kdbus/Makefile +index cd6b48ee41bf..f6d491251c25 100644 +--- a/Documentation/kdbus/Makefile ++++ b/Documentation/kdbus/Makefile +@@ -18,10 +18,10 @@ HTMLFILES := $(patsubst %.xml, %.html, $(XMLFILES)) + XMLTO_ARGS := -m $(obj)/stylesheet.xsl + + %.7: %.xml +- xmlto man $(XMLTO_ARGS) -o . $< ++ xmlto man $(XMLTO_ARGS) -o $(obj) $< + + %.html: %.xml +- xmlto html-nochunks $(XMLTO_ARGS) -o . $< ++ xmlto html-nochunks $(XMLTO_ARGS) -o $(obj) $< + + mandocs: $(MANFILES) + diff --git a/Documentation-kdbus-fix-operator-precedence-issue-in.patch b/Documentation-kdbus-fix-operator-precedence-issue-in.patch new file mode 100644 index 000000000..43eefba8d --- /dev/null +++ b/Documentation-kdbus-fix-operator-precedence-issue-in.patch @@ -0,0 +1,33 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Wed, 10 Jun 2015 00:00:01 +0300 +Subject: [PATCH] Documentation/kdbus: fix operator precedence issue in + KDBUS_ITEM_NEXT macro + +`item' argument in KDBUS_ITEM_NEXT macro example is not enclosed into +parentheses when the cast operator is applied, which leads to improper +type conversion if `item' is supplied as a complex expression, e.g. + + KDBUS_ITEM_NEXT(condition ? a : b) + +Use parentheses properly to guarantee right precedence. + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + Documentation/kdbus/kdbus.item.xml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/Documentation/kdbus/kdbus.item.xml b/Documentation/kdbus/kdbus.item.xml +index 09f8b903116f..b0eeeef995af 100644 +--- a/Documentation/kdbus/kdbus.item.xml ++++ b/Documentation/kdbus/kdbus.item.xml +@@ -69,7 +69,7 @@ + #define KDBUS_ALIGN8(val) (((val) + 7) & ~7) + + #define KDBUS_ITEM_NEXT(item) \ +- (typeof(item))(((uint8_t *)item) + KDBUS_ALIGN8((item)->size)) ++ (typeof(item))((uint8_t *)(item) + KDBUS_ALIGN8((item)->size)) + + #define KDBUS_ITEM_FOREACH(item, head, first) \ + for (item = (head)->first; \ diff --git a/Documentation-kdbus-fix-out-of-tree-builds.patch b/Documentation-kdbus-fix-out-of-tree-builds.patch new file mode 100644 index 000000000..bf9cf839b --- /dev/null +++ b/Documentation-kdbus-fix-out-of-tree-builds.patch @@ -0,0 +1,27 @@ +From: David Herrmann <dh.herrmann@gmail.com> +Date: Mon, 16 Mar 2015 10:17:11 +0100 +Subject: [PATCH] Documentation/kdbus: fix out-of-tree builds + +Don't use $(obj) to access source files, but use $(srctree)/$(src)/ +instead. This fixes build issues if you use O= with a directory other than +the source directory. + +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + Documentation/kdbus/Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/Documentation/kdbus/Makefile b/Documentation/kdbus/Makefile +index f6d491251c25..d8e6bf37d53b 100644 +--- a/Documentation/kdbus/Makefile ++++ b/Documentation/kdbus/Makefile +@@ -15,7 +15,7 @@ XMLFILES := $(addprefix $(obj)/,$(DOCS)) + MANFILES := $(patsubst %.xml, %.7, $(XMLFILES)) + HTMLFILES := $(patsubst %.xml, %.html, $(XMLFILES)) + +-XMLTO_ARGS := -m $(obj)/stylesheet.xsl ++XMLTO_ARGS := -m $(srctree)/$(src)/stylesheet.xsl + + %.7: %.xml + xmlto man $(XMLTO_ARGS) -o $(obj) $< diff --git a/Documentation-kdbus-replace-reply_cookie-with-cookie.patch b/Documentation-kdbus-replace-reply_cookie-with-cookie.patch new file mode 100644 index 000000000..02eaff497 --- /dev/null +++ b/Documentation-kdbus-replace-reply_cookie-with-cookie.patch @@ -0,0 +1,28 @@ +From: Lukasz Skalski <l.skalski@samsung.com> +Date: Mon, 16 Mar 2015 10:35:08 +0100 +Subject: [PATCH] Documentation/kdbus: replace 'reply_cookie' with + 'cookie_reply' + +The member field is called 'cookie_reply', fix the documentation which +incorrectly used 'reply_cookie'. + +Signed-off-by: Lukasz Skalski <l.skalski@samsung.com> +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + Documentation/kdbus/kdbus.message.xml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/Documentation/kdbus/kdbus.message.xml b/Documentation/kdbus/kdbus.message.xml +index c25000dcfbc7..5e7c7a3f537e 100644 +--- a/Documentation/kdbus/kdbus.message.xml ++++ b/Documentation/kdbus/kdbus.message.xml +@@ -393,7 +393,7 @@ struct kdbus_msg { + For a message to be accepted as reply, it must be a direct + message to the original sender (not a broadcast and not a + signal message), and its +- <varname>kdbus_msg.reply_cookie</varname> must match the ++ <varname>kdbus_msg.cookie_reply</varname> must match the + previous message's <varname>kdbus_msg.cookie</varname>. + </para><para> + Expected replies also temporarily open the policy of the diff --git a/Documentation-kdbus-support-quiet-builds.patch b/Documentation-kdbus-support-quiet-builds.patch new file mode 100644 index 000000000..b2f2eed15 --- /dev/null +++ b/Documentation-kdbus-support-quiet-builds.patch @@ -0,0 +1,44 @@ +From: David Herrmann <dh.herrmann@gmail.com> +Date: Mon, 16 Mar 2015 10:17:12 +0100 +Subject: [PATCH] Documentation/kdbus: support quiet builds + +Add support for quiet builds, just like Documentation/DocBook/Makefile +supports. + +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + Documentation/kdbus/Makefile | 16 +++++++++++++--- + 1 file changed, 13 insertions(+), 3 deletions(-) + +diff --git a/Documentation/kdbus/Makefile b/Documentation/kdbus/Makefile +index d8e6bf37d53b..af87641db416 100644 +--- a/Documentation/kdbus/Makefile ++++ b/Documentation/kdbus/Makefile +@@ -15,13 +15,23 @@ XMLFILES := $(addprefix $(obj)/,$(DOCS)) + MANFILES := $(patsubst %.xml, %.7, $(XMLFILES)) + HTMLFILES := $(patsubst %.xml, %.html, $(XMLFILES)) + +-XMLTO_ARGS := -m $(srctree)/$(src)/stylesheet.xsl ++XMLTO_ARGS := -m $(srctree)/$(src)/stylesheet.xsl --skip-validation + ++quiet_cmd_db2man = MAN $@ ++ cmd_db2man = xmlto man $(XMLTO_ARGS) -o $(obj) $< + %.7: %.xml +- xmlto man $(XMLTO_ARGS) -o $(obj) $< ++ @(which xmlto > /dev/null 2>&1) || \ ++ (echo "*** You need to install xmlto ***"; \ ++ exit 1) ++ $(call cmd,db2man) + ++quiet_cmd_db2html = HTML $@ ++ cmd_db2html = xmlto html-nochunks $(XMLTO_ARGS) -o $(obj) $< + %.html: %.xml +- xmlto html-nochunks $(XMLTO_ARGS) -o $(obj) $< ++ @(which xmlto > /dev/null 2>&1) || \ ++ (echo "*** You need to install xmlto ***"; \ ++ exit 1) ++ $(call cmd,db2html) + + mandocs: $(MANFILES) + diff --git a/Documentation-kdbus-use-parentheses-uniformly-in-KDB.patch b/Documentation-kdbus-use-parentheses-uniformly-in-KDB.patch new file mode 100644 index 000000000..61a5e3ec2 --- /dev/null +++ b/Documentation-kdbus-use-parentheses-uniformly-in-KDB.patch @@ -0,0 +1,32 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Wed, 10 Jun 2015 00:00:02 +0300 +Subject: [PATCH] Documentation/kdbus: use parentheses uniformly in + KDBUS_ITEM_FOREACH macro + +Enclose all arguments into parentheses to stay consistent across the +whole macro. + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + Documentation/kdbus/kdbus.item.xml | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/Documentation/kdbus/kdbus.item.xml b/Documentation/kdbus/kdbus.item.xml +index b0eeeef995af..ee09dfa443b8 100644 +--- a/Documentation/kdbus/kdbus.item.xml ++++ b/Documentation/kdbus/kdbus.item.xml +@@ -72,10 +72,10 @@ + (typeof(item))((uint8_t *)(item) + KDBUS_ALIGN8((item)->size)) + + #define KDBUS_ITEM_FOREACH(item, head, first) \ +- for (item = (head)->first; \ ++ for ((item) = (head)->first; \ + ((uint8_t *)(item) < (uint8_t *)(head) + (head)->size) && \ + ((uint8_t *)(item) >= (uint8_t *)(head)); \ +- item = KDBUS_ITEM_NEXT(item)) ++ (item) = KDBUS_ITEM_NEXT(item)) + ]]></programlisting> + </refsect2> + </refsect1> diff --git a/config-generic b/config-generic index a0f6909b7..7d522762a 100644 --- a/config-generic +++ b/config-generic @@ -67,6 +67,7 @@ CONFIG_NET_NS=y CONFIG_USER_NS=y CONFIG_POSIX_MQUEUE=y +CONFIG_KDBUS=m # CONFIG_PREEMPT_NONE is not set CONFIG_PREEMPT_VOLUNTARY=y # CONFIG_PREEMPT is not set diff --git a/kdbus-Fix-CONFIG_KDBUS-help-text.patch b/kdbus-Fix-CONFIG_KDBUS-help-text.patch new file mode 100644 index 000000000..cb5762866 --- /dev/null +++ b/kdbus-Fix-CONFIG_KDBUS-help-text.patch @@ -0,0 +1,35 @@ +From: Daniel Mack <daniel@zonque.org> +Date: Tue, 24 Mar 2015 19:51:55 +0100 +Subject: [PATCH] kdbus: Fix CONFIG_KDBUS help text + +Drop a left-over from the times when documentation lived in a +simple text file, which is no longer the case. Mention the +auto-generated man-pages and HTML files instead. + +Reported-by: Jiri Slaby <jslaby@suse.cz> +Signed-off-by: Daniel Mack <daniel@zonque.org> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + init/Kconfig | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/init/Kconfig b/init/Kconfig +index 02735f91836e..c4075075295d 100644 +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -268,10 +268,11 @@ config KDBUS + D-Bus is a system for low-latency, low-overhead, easy to use + interprocess communication (IPC). + +- See Documentation/kdbus.txt ++ See the man-pages and HTML files in Documentation/kdbus/ ++ that are generated by 'make mandocs' and 'make htmldocs'. + +- To compile this driver as a module, choose M here: the +- module will be called kdbus. ++ If you have an ordinary machine, select M here. The module ++ will be called kdbus. + + config CROSS_MEMORY_ATTACH + bool "Enable process_vm_readv/writev syscalls" diff --git a/kdbus-add-Makefile-Kconfig-and-MAINTAINERS-entry.patch b/kdbus-add-Makefile-Kconfig-and-MAINTAINERS-entry.patch new file mode 100644 index 000000000..567925020 --- /dev/null +++ b/kdbus-add-Makefile-Kconfig-and-MAINTAINERS-entry.patch @@ -0,0 +1,106 @@ +From: Daniel Mack <daniel@zonque.org> +Date: Thu, 11 Sep 2014 18:48:06 +0200 +Subject: [PATCH] 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: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Djalal Harouni <tixxdz@opendz.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + MAINTAINERS | 13 +++++++++++++ + init/Kconfig | 12 ++++++++++++ + ipc/Makefile | 2 +- + ipc/kdbus/Makefile | 22 ++++++++++++++++++++++ + 4 files changed, 48 insertions(+), 1 deletion(-) + create mode 100644 ipc/kdbus/Makefile + +diff --git a/MAINTAINERS b/MAINTAINERS +index 8133cefb6b6e..11a76301dde3 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -5780,6 +5780,19 @@ 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: ipc/kdbus/* ++F: samples/kdbus/* ++F: Documentation/kdbus/* ++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> +diff --git a/init/Kconfig b/init/Kconfig +index 860ca236975f..02735f91836e 100644 +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -261,6 +261,18 @@ config POSIX_MQUEUE_SYSCTL + depends on SYSCTL + default y + ++config KDBUS ++ tristate "kdbus interprocess communication" ++ depends on TMPFS ++ help ++ D-Bus is a system for low-latency, low-overhead, easy to use ++ interprocess communication (IPC). ++ ++ See Documentation/kdbus.txt ++ ++ To compile this driver as a module, choose M here: the ++ module will be called kdbus. ++ + config CROSS_MEMORY_ATTACH + bool "Enable process_vm_readv/writev syscalls" + depends on MMU +diff --git a/ipc/Makefile b/ipc/Makefile +index 86c7300ecdf5..68ec4167d11b 100644 +--- a/ipc/Makefile ++++ b/ipc/Makefile +@@ -9,4 +9,4 @@ obj_mq-$(CONFIG_COMPAT) += compat_mq.o + obj-$(CONFIG_POSIX_MQUEUE) += mqueue.o msgutil.o $(obj_mq-y) + obj-$(CONFIG_IPC_NS) += namespace.o + obj-$(CONFIG_POSIX_MQUEUE_SYSCTL) += mq_sysctl.o +- ++obj-$(CONFIG_KDBUS) += kdbus/ +diff --git a/ipc/kdbus/Makefile b/ipc/kdbus/Makefile +new file mode 100644 +index 000000000000..7ee9271e1449 +--- /dev/null ++++ b/ipc/kdbus/Makefile +@@ -0,0 +1,22 @@ ++kdbus-y := \ ++ bus.o \ ++ connection.o \ ++ endpoint.o \ ++ fs.o \ ++ handle.o \ ++ item.o \ ++ main.o \ ++ match.o \ ++ message.o \ ++ metadata.o \ ++ names.o \ ++ node.o \ ++ notify.o \ ++ domain.o \ ++ policy.o \ ++ pool.o \ ++ reply.o \ ++ queue.o \ ++ util.o ++ ++obj-$(CONFIG_KDBUS) += kdbus.o diff --git a/kdbus-add-code-for-buses-domains-and-endpoints.patch b/kdbus-add-code-for-buses-domains-and-endpoints.patch new file mode 100644 index 000000000..7ed9347bc --- /dev/null +++ b/kdbus-add-code-for-buses-domains-and-endpoints.patch @@ -0,0 +1,1466 @@ +From: Daniel Mack <daniel@zonque.org> +Date: Thu, 11 Sep 2014 18:59:39 +0200 +Subject: [PATCH] kdbus: add code for buses, domains and endpoints + +Add the logic to handle the following entities: + +Domain: + A domain is an unamed object containing a number of buses. A + domain is automatically created when an instance of kdbusfs + is mounted, and destroyed when it is unmounted. + Every domain offers its own 'control' device node to create + buses. Domains are isolated from each other. + +Bus: + A bus is a named object inside a domain. Clients exchange messages + over a bus. Multiple buses themselves have no connection to each + other; messages can only be exchanged on the same bus. The default + entry point to a bus, where clients establish the connection to, is + the "bus" device node /sys/fs/kdbus/<bus name>/bus. Common operating + system setups create one "system bus" per system, and one "user + bus" for every logged-in user. Applications or services may create + their own private named buses. + +Endpoint: + An endpoint provides the device node to talk to a bus. Opening an + endpoint creates a new connection to the bus to which the endpoint + belongs. Every bus has a default endpoint called "bus". A bus can + optionally offer additional endpoints with custom names to provide + a restricted access to the same bus. Custom endpoints carry + additional policy which can be used to give sandboxed processes + only a locked-down, limited, filtered access to the same bus. + +See kdbus(7), kdbus.bus(7), kdbus.endpoint(7) and kdbus.fs(7) +for more details. + +Signed-off-by: Daniel Mack <daniel@zonque.org> +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Djalal Harouni <tixxdz@opendz.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + ipc/kdbus/bus.c | 560 +++++++++++++++++++++++++++++++++++++++++++++++++++ + ipc/kdbus/bus.h | 101 ++++++++++ + ipc/kdbus/domain.c | 296 +++++++++++++++++++++++++++ + ipc/kdbus/domain.h | 77 +++++++ + ipc/kdbus/endpoint.c | 275 +++++++++++++++++++++++++ + ipc/kdbus/endpoint.h | 67 ++++++ + 6 files changed, 1376 insertions(+) + create mode 100644 ipc/kdbus/bus.c + create mode 100644 ipc/kdbus/bus.h + create mode 100644 ipc/kdbus/domain.c + create mode 100644 ipc/kdbus/domain.h + create mode 100644 ipc/kdbus/endpoint.c + create mode 100644 ipc/kdbus/endpoint.h + +diff --git a/ipc/kdbus/bus.c b/ipc/kdbus/bus.c +new file mode 100644 +index 000000000000..9d0679eb59f6 +--- /dev/null ++++ b/ipc/kdbus/bus.c +@@ -0,0 +1,560 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#include <linux/fs.h> ++#include <linux/hashtable.h> ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/random.h> ++#include <linux/sched.h> ++#include <linux/sizes.h> ++#include <linux/slab.h> ++#include <linux/uaccess.h> ++#include <linux/uio.h> ++ ++#include "bus.h" ++#include "notify.h" ++#include "connection.h" ++#include "domain.h" ++#include "endpoint.h" ++#include "handle.h" ++#include "item.h" ++#include "match.h" ++#include "message.h" ++#include "metadata.h" ++#include "names.h" ++#include "policy.h" ++#include "util.h" ++ ++static void kdbus_bus_free(struct kdbus_node *node) ++{ ++ struct kdbus_bus *bus = container_of(node, struct kdbus_bus, node); ++ ++ WARN_ON(!list_empty(&bus->monitors_list)); ++ WARN_ON(!hash_empty(bus->conn_hash)); ++ ++ kdbus_notify_free(bus); ++ ++ kdbus_user_unref(bus->creator); ++ kdbus_name_registry_free(bus->name_registry); ++ kdbus_domain_unref(bus->domain); ++ kdbus_policy_db_clear(&bus->policy_db); ++ kdbus_meta_proc_unref(bus->creator_meta); ++ kfree(bus); ++} ++ ++static void kdbus_bus_release(struct kdbus_node *node, bool was_active) ++{ ++ struct kdbus_bus *bus = container_of(node, struct kdbus_bus, node); ++ ++ if (was_active) ++ atomic_dec(&bus->creator->buses); ++} ++ ++static struct kdbus_bus *kdbus_bus_new(struct kdbus_domain *domain, ++ const char *name, ++ struct kdbus_bloom_parameter *bloom, ++ const u64 *pattach_owner, ++ const u64 *pattach_recv, ++ u64 flags, kuid_t uid, kgid_t gid) ++{ ++ struct kdbus_bus *b; ++ u64 attach_owner; ++ u64 attach_recv; ++ int ret; ++ ++ if (bloom->size < 8 || bloom->size > KDBUS_BUS_BLOOM_MAX_SIZE || ++ !KDBUS_IS_ALIGNED8(bloom->size) || bloom->n_hash < 1) ++ return ERR_PTR(-EINVAL); ++ ++ ret = kdbus_sanitize_attach_flags(pattach_recv ? *pattach_recv : 0, ++ &attach_recv); ++ if (ret < 0) ++ return ERR_PTR(ret); ++ ++ ret = kdbus_sanitize_attach_flags(pattach_owner ? *pattach_owner : 0, ++ &attach_owner); ++ if (ret < 0) ++ return ERR_PTR(ret); ++ ++ ret = kdbus_verify_uid_prefix(name, domain->user_namespace, uid); ++ if (ret < 0) ++ return ERR_PTR(ret); ++ ++ b = kzalloc(sizeof(*b), GFP_KERNEL); ++ if (!b) ++ return ERR_PTR(-ENOMEM); ++ ++ kdbus_node_init(&b->node, KDBUS_NODE_BUS); ++ ++ b->node.free_cb = kdbus_bus_free; ++ b->node.release_cb = kdbus_bus_release; ++ b->node.uid = uid; ++ b->node.gid = gid; ++ b->node.mode = S_IRUSR | S_IXUSR; ++ ++ if (flags & (KDBUS_MAKE_ACCESS_GROUP | KDBUS_MAKE_ACCESS_WORLD)) ++ b->node.mode |= S_IRGRP | S_IXGRP; ++ if (flags & KDBUS_MAKE_ACCESS_WORLD) ++ b->node.mode |= S_IROTH | S_IXOTH; ++ ++ b->id = atomic64_inc_return(&domain->last_id); ++ b->bus_flags = flags; ++ b->attach_flags_req = attach_recv; ++ b->attach_flags_owner = attach_owner; ++ generate_random_uuid(b->id128); ++ b->bloom = *bloom; ++ b->domain = kdbus_domain_ref(domain); ++ ++ kdbus_policy_db_init(&b->policy_db); ++ ++ init_rwsem(&b->conn_rwlock); ++ hash_init(b->conn_hash); ++ INIT_LIST_HEAD(&b->monitors_list); ++ ++ INIT_LIST_HEAD(&b->notify_list); ++ spin_lock_init(&b->notify_lock); ++ mutex_init(&b->notify_flush_lock); ++ ++ ret = kdbus_node_link(&b->node, &domain->node, name); ++ if (ret < 0) ++ goto exit_unref; ++ ++ /* cache the metadata/credentials of the creator */ ++ b->creator_meta = kdbus_meta_proc_new(); ++ if (IS_ERR(b->creator_meta)) { ++ ret = PTR_ERR(b->creator_meta); ++ b->creator_meta = NULL; ++ goto exit_unref; ++ } ++ ++ ret = kdbus_meta_proc_collect(b->creator_meta, ++ KDBUS_ATTACH_CREDS | ++ KDBUS_ATTACH_PIDS | ++ KDBUS_ATTACH_AUXGROUPS | ++ KDBUS_ATTACH_TID_COMM | ++ KDBUS_ATTACH_PID_COMM | ++ KDBUS_ATTACH_EXE | ++ KDBUS_ATTACH_CMDLINE | ++ KDBUS_ATTACH_CGROUP | ++ KDBUS_ATTACH_CAPS | ++ KDBUS_ATTACH_SECLABEL | ++ KDBUS_ATTACH_AUDIT); ++ if (ret < 0) ++ goto exit_unref; ++ ++ b->name_registry = kdbus_name_registry_new(); ++ if (IS_ERR(b->name_registry)) { ++ ret = PTR_ERR(b->name_registry); ++ b->name_registry = NULL; ++ goto exit_unref; ++ } ++ ++ /* ++ * Bus-limits of the creator are accounted on its real UID, just like ++ * all other per-user limits. ++ */ ++ b->creator = kdbus_user_lookup(domain, current_uid()); ++ if (IS_ERR(b->creator)) { ++ ret = PTR_ERR(b->creator); ++ b->creator = NULL; ++ goto exit_unref; ++ } ++ ++ return b; ++ ++exit_unref: ++ kdbus_node_deactivate(&b->node); ++ kdbus_node_unref(&b->node); ++ return ERR_PTR(ret); ++} ++ ++/** ++ * kdbus_bus_ref() - increase the reference counter of a kdbus_bus ++ * @bus: The bus to reference ++ * ++ * Every user of a bus, except for its creator, must add a reference to the ++ * kdbus_bus using this function. ++ * ++ * Return: the bus itself ++ */ ++struct kdbus_bus *kdbus_bus_ref(struct kdbus_bus *bus) ++{ ++ if (bus) ++ kdbus_node_ref(&bus->node); ++ return bus; ++} ++ ++/** ++ * kdbus_bus_unref() - decrease the reference counter of a kdbus_bus ++ * @bus: The bus to unref ++ * ++ * Release a reference. If the reference count drops to 0, the bus will be ++ * freed. ++ * ++ * Return: NULL ++ */ ++struct kdbus_bus *kdbus_bus_unref(struct kdbus_bus *bus) ++{ ++ if (bus) ++ kdbus_node_unref(&bus->node); ++ return NULL; ++} ++ ++/** ++ * kdbus_bus_find_conn_by_id() - find a connection with a given id ++ * @bus: The bus to look for the connection ++ * @id: The 64-bit connection id ++ * ++ * Looks up a connection with a given id. The returned connection ++ * is ref'ed, and needs to be unref'ed by the user. Returns NULL if ++ * the connection can't be found. ++ */ ++struct kdbus_conn *kdbus_bus_find_conn_by_id(struct kdbus_bus *bus, u64 id) ++{ ++ struct kdbus_conn *conn, *found = NULL; ++ ++ down_read(&bus->conn_rwlock); ++ hash_for_each_possible(bus->conn_hash, conn, hentry, id) ++ if (conn->id == id) { ++ found = kdbus_conn_ref(conn); ++ break; ++ } ++ up_read(&bus->conn_rwlock); ++ ++ return found; ++} ++ ++/** ++ * kdbus_bus_broadcast() - send a message to all subscribed connections ++ * @bus: The bus the connections are connected to ++ * @conn_src: The source connection, may be %NULL for kernel notifications ++ * @kmsg: The message to send. ++ * ++ * Send @kmsg to all connections that are currently active on the bus. ++ * Connections must still have matches installed in order to let the message ++ * pass. ++ * ++ * The caller must hold the name-registry lock of @bus. ++ */ ++void kdbus_bus_broadcast(struct kdbus_bus *bus, ++ struct kdbus_conn *conn_src, ++ struct kdbus_kmsg *kmsg) ++{ ++ struct kdbus_conn *conn_dst; ++ unsigned int i; ++ int ret; ++ ++ lockdep_assert_held(&bus->name_registry->rwlock); ++ ++ /* ++ * Make sure broadcast are queued on monitors before we send it out to ++ * anyone else. Otherwise, connections might react to broadcasts before ++ * the monitor gets the broadcast queued. In the worst case, the ++ * monitor sees a reaction to the broadcast before the broadcast itself. ++ * We don't give ordering guarantees across connections (and monitors ++ * can re-construct order via sequence numbers), but we should at least ++ * try to avoid re-ordering for monitors. ++ */ ++ kdbus_bus_eavesdrop(bus, conn_src, kmsg); ++ ++ down_read(&bus->conn_rwlock); ++ hash_for_each(bus->conn_hash, i, conn_dst, hentry) { ++ if (conn_dst->id == kmsg->msg.src_id) ++ continue; ++ if (!kdbus_conn_is_ordinary(conn_dst)) ++ continue; ++ ++ /* ++ * Check if there is a match for the kmsg object in ++ * the destination connection match db ++ */ ++ if (!kdbus_match_db_match_kmsg(conn_dst->match_db, conn_src, ++ kmsg)) ++ continue; ++ ++ if (conn_src) { ++ u64 attach_flags; ++ ++ /* ++ * Anyone can send broadcasts, as they have no ++ * destination. But a receiver needs TALK access to ++ * the sender in order to receive broadcasts. ++ */ ++ if (!kdbus_conn_policy_talk(conn_dst, NULL, conn_src)) ++ continue; ++ ++ attach_flags = kdbus_meta_calc_attach_flags(conn_src, ++ conn_dst); ++ ++ /* ++ * Keep sending messages even if we cannot acquire the ++ * requested metadata. It's up to the receiver to drop ++ * messages that lack expected metadata. ++ */ ++ if (!conn_src->faked_meta) ++ kdbus_meta_proc_collect(kmsg->proc_meta, ++ attach_flags); ++ kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, conn_src, ++ attach_flags); ++ } else { ++ /* ++ * Check if there is a policy db that prevents the ++ * destination connection from receiving this kernel ++ * notification ++ */ ++ if (!kdbus_conn_policy_see_notification(conn_dst, NULL, ++ kmsg)) ++ continue; ++ } ++ ++ ret = kdbus_conn_entry_insert(conn_src, conn_dst, kmsg, NULL); ++ if (ret < 0) ++ kdbus_conn_lost_message(conn_dst); ++ } ++ up_read(&bus->conn_rwlock); ++} ++ ++/** ++ * kdbus_bus_eavesdrop() - send a message to all subscribed monitors ++ * @bus: The bus the monitors are connected to ++ * @conn_src: The source connection, may be %NULL for kernel notifications ++ * @kmsg: The message to send. ++ * ++ * Send @kmsg to all monitors that are currently active on the bus. Monitors ++ * must still have matches installed in order to let the message pass. ++ * ++ * The caller must hold the name-registry lock of @bus. ++ */ ++void kdbus_bus_eavesdrop(struct kdbus_bus *bus, ++ struct kdbus_conn *conn_src, ++ struct kdbus_kmsg *kmsg) ++{ ++ struct kdbus_conn *conn_dst; ++ int ret; ++ ++ /* ++ * Monitor connections get all messages; ignore possible errors ++ * when sending messages to monitor connections. ++ */ ++ ++ lockdep_assert_held(&bus->name_registry->rwlock); ++ ++ down_read(&bus->conn_rwlock); ++ list_for_each_entry(conn_dst, &bus->monitors_list, monitor_entry) { ++ /* ++ * Collect metadata requested by the destination connection. ++ * Ignore errors, as receivers need to check metadata ++ * availability, anyway. So it's still better to send messages ++ * that lack data, than to skip it entirely. ++ */ ++ if (conn_src) { ++ u64 attach_flags; ++ ++ attach_flags = kdbus_meta_calc_attach_flags(conn_src, ++ conn_dst); ++ if (!conn_src->faked_meta) ++ kdbus_meta_proc_collect(kmsg->proc_meta, ++ attach_flags); ++ kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, conn_src, ++ attach_flags); ++ } ++ ++ ret = kdbus_conn_entry_insert(conn_src, conn_dst, kmsg, NULL); ++ if (ret < 0) ++ kdbus_conn_lost_message(conn_dst); ++ } ++ up_read(&bus->conn_rwlock); ++} ++ ++/** ++ * kdbus_cmd_bus_make() - handle KDBUS_CMD_BUS_MAKE ++ * @domain: domain to operate on ++ * @argp: command payload ++ * ++ * Return: Newly created bus on success, ERR_PTR on failure. ++ */ ++struct kdbus_bus *kdbus_cmd_bus_make(struct kdbus_domain *domain, ++ void __user *argp) ++{ ++ struct kdbus_bus *bus = NULL; ++ struct kdbus_cmd *cmd; ++ struct kdbus_ep *ep = NULL; ++ int ret; ++ ++ struct kdbus_arg argv[] = { ++ { .type = KDBUS_ITEM_NEGOTIATE }, ++ { .type = KDBUS_ITEM_MAKE_NAME, .mandatory = true }, ++ { .type = KDBUS_ITEM_BLOOM_PARAMETER, .mandatory = true }, ++ { .type = KDBUS_ITEM_ATTACH_FLAGS_SEND }, ++ { .type = KDBUS_ITEM_ATTACH_FLAGS_RECV }, ++ }; ++ struct kdbus_args args = { ++ .allowed_flags = KDBUS_FLAG_NEGOTIATE | ++ KDBUS_MAKE_ACCESS_GROUP | ++ KDBUS_MAKE_ACCESS_WORLD, ++ .argv = argv, ++ .argc = ARRAY_SIZE(argv), ++ }; ++ ++ ret = kdbus_args_parse(&args, argp, &cmd); ++ if (ret < 0) ++ return ERR_PTR(ret); ++ if (ret > 0) ++ return NULL; ++ ++ bus = kdbus_bus_new(domain, ++ argv[1].item->str, &argv[2].item->bloom_parameter, ++ argv[3].item ? argv[3].item->data64 : NULL, ++ argv[4].item ? argv[4].item->data64 : NULL, ++ cmd->flags, current_euid(), current_egid()); ++ if (IS_ERR(bus)) { ++ ret = PTR_ERR(bus); ++ bus = NULL; ++ goto exit; ++ } ++ ++ if (atomic_inc_return(&bus->creator->buses) > KDBUS_USER_MAX_BUSES) { ++ atomic_dec(&bus->creator->buses); ++ ret = -EMFILE; ++ goto exit; ++ } ++ ++ if (!kdbus_node_activate(&bus->node)) { ++ atomic_dec(&bus->creator->buses); ++ ret = -ESHUTDOWN; ++ goto exit; ++ } ++ ++ ep = kdbus_ep_new(bus, "bus", cmd->flags, bus->node.uid, bus->node.gid, ++ false); ++ if (IS_ERR(ep)) { ++ ret = PTR_ERR(ep); ++ ep = NULL; ++ goto exit; ++ } ++ ++ if (!kdbus_node_activate(&ep->node)) { ++ ret = -ESHUTDOWN; ++ goto exit; ++ } ++ ++ /* ++ * Drop our own reference, effectively causing the endpoint to be ++ * deactivated and released when the parent bus is. ++ */ ++ ep = kdbus_ep_unref(ep); ++ ++exit: ++ ret = kdbus_args_clear(&args, ret); ++ if (ret < 0) { ++ if (ep) { ++ kdbus_node_deactivate(&ep->node); ++ kdbus_ep_unref(ep); ++ } ++ if (bus) { ++ kdbus_node_deactivate(&bus->node); ++ kdbus_bus_unref(bus); ++ } ++ return ERR_PTR(ret); ++ } ++ return bus; ++} ++ ++/** ++ * kdbus_cmd_bus_creator_info() - handle KDBUS_CMD_BUS_CREATOR_INFO ++ * @conn: connection to operate on ++ * @argp: command payload ++ * ++ * Return: 0 on success, negative error code on failure. ++ */ ++int kdbus_cmd_bus_creator_info(struct kdbus_conn *conn, void __user *argp) ++{ ++ struct kdbus_cmd_info *cmd; ++ struct kdbus_bus *bus = conn->ep->bus; ++ struct kdbus_pool_slice *slice = NULL; ++ struct kdbus_item_header item_hdr; ++ struct kdbus_info info = {}; ++ size_t meta_size, name_len; ++ struct kvec kvec[5]; ++ u64 hdr_size = 0; ++ u64 attach_flags; ++ size_t cnt = 0; ++ int ret; ++ ++ struct kdbus_arg argv[] = { ++ { .type = KDBUS_ITEM_NEGOTIATE }, ++ }; ++ struct kdbus_args args = { ++ .allowed_flags = KDBUS_FLAG_NEGOTIATE, ++ .argv = argv, ++ .argc = ARRAY_SIZE(argv), ++ }; ++ ++ ret = kdbus_args_parse(&args, argp, &cmd); ++ if (ret != 0) ++ return ret; ++ ++ ret = kdbus_sanitize_attach_flags(cmd->attach_flags, &attach_flags); ++ if (ret < 0) ++ goto exit; ++ ++ attach_flags &= bus->attach_flags_owner; ++ ++ ret = kdbus_meta_export_prepare(bus->creator_meta, NULL, ++ &attach_flags, &meta_size); ++ if (ret < 0) ++ goto exit; ++ ++ name_len = strlen(bus->node.name) + 1; ++ info.id = bus->id; ++ info.flags = bus->bus_flags; ++ item_hdr.type = KDBUS_ITEM_MAKE_NAME; ++ item_hdr.size = KDBUS_ITEM_HEADER_SIZE + name_len; ++ ++ kdbus_kvec_set(&kvec[cnt++], &info, sizeof(info), &hdr_size); ++ kdbus_kvec_set(&kvec[cnt++], &item_hdr, sizeof(item_hdr), &hdr_size); ++ kdbus_kvec_set(&kvec[cnt++], bus->node.name, name_len, &hdr_size); ++ cnt += !!kdbus_kvec_pad(&kvec[cnt], &hdr_size); ++ ++ slice = kdbus_pool_slice_alloc(conn->pool, hdr_size + meta_size, false); ++ if (IS_ERR(slice)) { ++ ret = PTR_ERR(slice); ++ slice = NULL; ++ goto exit; ++ } ++ ++ ret = kdbus_meta_export(bus->creator_meta, NULL, attach_flags, ++ slice, hdr_size, &meta_size); ++ if (ret < 0) ++ goto exit; ++ ++ info.size = hdr_size + meta_size; ++ ++ ret = kdbus_pool_slice_copy_kvec(slice, 0, kvec, cnt, hdr_size); ++ if (ret < 0) ++ goto exit; ++ ++ kdbus_pool_slice_publish(slice, &cmd->offset, &cmd->info_size); ++ ++ if (kdbus_member_set_user(&cmd->offset, argp, typeof(*cmd), offset) || ++ kdbus_member_set_user(&cmd->info_size, argp, ++ typeof(*cmd), info_size)) ++ ret = -EFAULT; ++ ++exit: ++ kdbus_pool_slice_release(slice); ++ ++ return kdbus_args_clear(&args, ret); ++} +diff --git a/ipc/kdbus/bus.h b/ipc/kdbus/bus.h +new file mode 100644 +index 000000000000..5bea5ef768f1 +--- /dev/null ++++ b/ipc/kdbus/bus.h +@@ -0,0 +1,101 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> ++ * ++ * 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/list.h> ++#include <linux/mutex.h> ++#include <linux/rwsem.h> ++#include <linux/spinlock.h> ++#include <uapi/linux/kdbus.h> ++ ++#include "metadata.h" ++#include "names.h" ++#include "node.h" ++#include "policy.h" ++ ++struct kdbus_conn; ++struct kdbus_domain; ++struct kdbus_kmsg; ++struct kdbus_user; ++ ++/** ++ * struct kdbus_bus - bus in a domain ++ * @node: kdbus_node ++ * @id: ID of this bus in the domain ++ * @bus_flags: Simple pass-through flags from userspace to userspace ++ * @attach_flags_req: KDBUS_ATTACH_* flags required by connecting peers ++ * @attach_flags_owner: KDBUS_ATTACH_* flags of bus creator that other ++ * connections can see or query ++ * @id128: Unique random 128 bit ID of this bus ++ * @bloom: Bloom parameters ++ * @domain: Domain of this bus ++ * @creator: Creator of the bus ++ * @creator_meta: Meta information about the bus creator ++ * @policy_db: Policy database for this bus ++ * @name_registry: Name registry of this bus ++ * @conn_rwlock: Read/Write lock for all lists of child connections ++ * @conn_hash: Map of connection IDs ++ * @monitors_list: Connections that monitor this bus ++ * @notify_list: List of pending kernel-generated messages ++ * @notify_lock: Notification list lock ++ * @notify_flush_lock: Notification flushing lock ++ */ ++struct kdbus_bus { ++ struct kdbus_node node; ++ ++ /* static */ ++ u64 id; ++ u64 bus_flags; ++ u64 attach_flags_req; ++ u64 attach_flags_owner; ++ u8 id128[16]; ++ struct kdbus_bloom_parameter bloom; ++ struct kdbus_domain *domain; ++ struct kdbus_user *creator; ++ struct kdbus_meta_proc *creator_meta; ++ ++ /* protected by own locks */ ++ struct kdbus_policy_db policy_db; ++ struct kdbus_name_registry *name_registry; ++ ++ /* protected by conn_rwlock */ ++ struct rw_semaphore conn_rwlock; ++ DECLARE_HASHTABLE(conn_hash, 8); ++ struct list_head monitors_list; ++ ++ /* protected by notify_lock */ ++ struct list_head notify_list; ++ spinlock_t notify_lock; ++ struct mutex notify_flush_lock; ++}; ++ ++struct kdbus_bus *kdbus_bus_ref(struct kdbus_bus *bus); ++struct kdbus_bus *kdbus_bus_unref(struct kdbus_bus *bus); ++ ++struct kdbus_conn *kdbus_bus_find_conn_by_id(struct kdbus_bus *bus, u64 id); ++void kdbus_bus_broadcast(struct kdbus_bus *bus, ++ struct kdbus_conn *conn_src, ++ struct kdbus_kmsg *kmsg); ++void kdbus_bus_eavesdrop(struct kdbus_bus *bus, ++ struct kdbus_conn *conn_src, ++ struct kdbus_kmsg *kmsg); ++ ++struct kdbus_bus *kdbus_cmd_bus_make(struct kdbus_domain *domain, ++ void __user *argp); ++int kdbus_cmd_bus_creator_info(struct kdbus_conn *conn, void __user *argp); ++ ++#endif +diff --git a/ipc/kdbus/domain.c b/ipc/kdbus/domain.c +new file mode 100644 +index 000000000000..ac9f760c150d +--- /dev/null ++++ b/ipc/kdbus/domain.c +@@ -0,0 +1,296 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> ++ * ++ * 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/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" ++ ++static void kdbus_domain_control_free(struct kdbus_node *node) ++{ ++ kfree(node); ++} ++ ++static struct kdbus_node *kdbus_domain_control_new(struct kdbus_domain *domain, ++ unsigned int access) ++{ ++ struct kdbus_node *node; ++ int ret; ++ ++ node = kzalloc(sizeof(*node), GFP_KERNEL); ++ if (!node) ++ return ERR_PTR(-ENOMEM); ++ ++ kdbus_node_init(node, KDBUS_NODE_CONTROL); ++ ++ node->free_cb = kdbus_domain_control_free; ++ node->mode = domain->node.mode; ++ node->mode = S_IRUSR | S_IWUSR; ++ if (access & (KDBUS_MAKE_ACCESS_GROUP | KDBUS_MAKE_ACCESS_WORLD)) ++ node->mode |= S_IRGRP | S_IWGRP; ++ if (access & KDBUS_MAKE_ACCESS_WORLD) ++ node->mode |= S_IROTH | S_IWOTH; ++ ++ ret = kdbus_node_link(node, &domain->node, "control"); ++ if (ret < 0) ++ goto exit_free; ++ ++ return node; ++ ++exit_free: ++ kdbus_node_deactivate(node); ++ kdbus_node_unref(node); ++ return ERR_PTR(ret); ++} ++ ++static void kdbus_domain_free(struct kdbus_node *node) ++{ ++ struct kdbus_domain *domain = ++ container_of(node, struct kdbus_domain, node); ++ ++ put_user_ns(domain->user_namespace); ++ ida_destroy(&domain->user_ida); ++ idr_destroy(&domain->user_idr); ++ kfree(domain); ++} ++ ++/** ++ * kdbus_domain_new() - create a new domain ++ * @access: The access mode for this node (KDBUS_MAKE_ACCESS_*) ++ * ++ * Return: a new kdbus_domain on success, ERR_PTR on failure ++ */ ++struct kdbus_domain *kdbus_domain_new(unsigned int access) ++{ ++ struct kdbus_domain *d; ++ int ret; ++ ++ d = kzalloc(sizeof(*d), GFP_KERNEL); ++ if (!d) ++ return ERR_PTR(-ENOMEM); ++ ++ kdbus_node_init(&d->node, KDBUS_NODE_DOMAIN); ++ ++ d->node.free_cb = kdbus_domain_free; ++ d->node.mode = S_IRUSR | S_IXUSR; ++ if (access & (KDBUS_MAKE_ACCESS_GROUP | KDBUS_MAKE_ACCESS_WORLD)) ++ d->node.mode |= S_IRGRP | S_IXGRP; ++ if (access & KDBUS_MAKE_ACCESS_WORLD) ++ d->node.mode |= S_IROTH | S_IXOTH; ++ ++ mutex_init(&d->lock); ++ idr_init(&d->user_idr); ++ ida_init(&d->user_ida); ++ ++ /* Pin user namespace so we can guarantee domain-unique bus * names. */ ++ d->user_namespace = get_user_ns(current_user_ns()); ++ ++ ret = kdbus_node_link(&d->node, NULL, NULL); ++ if (ret < 0) ++ goto exit_unref; ++ ++ return d; ++ ++exit_unref: ++ kdbus_node_deactivate(&d->node); ++ kdbus_node_unref(&d->node); ++ return ERR_PTR(ret); ++} ++ ++/** ++ * kdbus_domain_ref() - take a domain reference ++ * @domain: Domain ++ * ++ * Return: the domain itself ++ */ ++struct kdbus_domain *kdbus_domain_ref(struct kdbus_domain *domain) ++{ ++ if (domain) ++ kdbus_node_ref(&domain->node); ++ return 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) ++ kdbus_node_unref(&domain->node); ++ return NULL; ++} ++ ++/** ++ * kdbus_domain_populate() - populate static domain nodes ++ * @domain: domain to populate ++ * @access: KDBUS_MAKE_ACCESS_* access restrictions for new nodes ++ * ++ * Allocate and activate static sub-nodes of the given domain. This will fail if ++ * you call it on a non-active node or if the domain was already populated. ++ * ++ * Return: 0 on success, negative error code on failure. ++ */ ++int kdbus_domain_populate(struct kdbus_domain *domain, unsigned int access) ++{ ++ struct kdbus_node *control; ++ ++ /* ++ * Create a control-node for this domain. We drop our own reference ++ * immediately, effectively causing the node to be deactivated and ++ * released when the parent domain is. ++ */ ++ control = kdbus_domain_control_new(domain, access); ++ if (IS_ERR(control)) ++ return PTR_ERR(control); ++ ++ kdbus_node_activate(control); ++ kdbus_node_unref(control); ++ return 0; ++} ++ ++/** ++ * kdbus_user_lookup() - lookup a kdbus_user object ++ * @domain: domain of the user ++ * @uid: uid of the user; INVALID_UID for an anon user ++ * ++ * Lookup the kdbus user accounting object for the given domain. If INVALID_UID ++ * is passed, a new anonymous user is created which is private to the caller. ++ * ++ * Return: The user object is returned, ERR_PTR on failure. ++ */ ++struct kdbus_user *kdbus_user_lookup(struct kdbus_domain *domain, kuid_t uid) ++{ ++ struct kdbus_user *u = NULL, *old = NULL; ++ int ret; ++ ++ mutex_lock(&domain->lock); ++ ++ if (uid_valid(uid)) { ++ old = idr_find(&domain->user_idr, __kuid_val(uid)); ++ /* ++ * If the object is about to be destroyed, ignore it and ++ * replace the slot in the IDR later on. ++ */ ++ if (old && kref_get_unless_zero(&old->kref)) { ++ mutex_unlock(&domain->lock); ++ return old; ++ } ++ } ++ ++ u = kzalloc(sizeof(*u), GFP_KERNEL); ++ if (!u) { ++ ret = -ENOMEM; ++ goto exit; ++ } ++ ++ kref_init(&u->kref); ++ u->domain = kdbus_domain_ref(domain); ++ u->uid = uid; ++ atomic_set(&u->buses, 0); ++ atomic_set(&u->connections, 0); ++ ++ if (uid_valid(uid)) { ++ if (old) { ++ idr_replace(&domain->user_idr, u, __kuid_val(uid)); ++ old->uid = INVALID_UID; /* mark old as removed */ ++ } else { ++ ret = idr_alloc(&domain->user_idr, u, __kuid_val(uid), ++ __kuid_val(uid) + 1, GFP_KERNEL); ++ if (ret < 0) ++ goto exit; ++ } ++ } ++ ++ /* ++ * Allocate the smallest possible index for this user; used ++ * in arrays for accounting user quota in receiver queues. ++ */ ++ ret = ida_simple_get(&domain->user_ida, 1, 0, GFP_KERNEL); ++ if (ret < 0) ++ goto exit; ++ ++ u->id = ret; ++ mutex_unlock(&domain->lock); ++ return u; ++ ++exit: ++ if (u) { ++ if (uid_valid(u->uid)) ++ idr_remove(&domain->user_idr, __kuid_val(u->uid)); ++ kdbus_domain_unref(u->domain); ++ kfree(u); ++ } ++ mutex_unlock(&domain->lock); ++ return ERR_PTR(ret); ++} ++ ++static void __kdbus_user_free(struct kref *kref) ++{ ++ struct kdbus_user *user = container_of(kref, struct kdbus_user, kref); ++ ++ WARN_ON(atomic_read(&user->buses) > 0); ++ WARN_ON(atomic_read(&user->connections) > 0); ++ ++ mutex_lock(&user->domain->lock); ++ ida_simple_remove(&user->domain->user_ida, user->id); ++ if (uid_valid(user->uid)) ++ idr_remove(&user->domain->user_idr, __kuid_val(user->uid)); ++ mutex_unlock(&user->domain->lock); ++ ++ kdbus_domain_unref(user->domain); ++ kfree(user); ++} ++ ++/** ++ * kdbus_user_ref() - take a user reference ++ * @u: User ++ * ++ * Return: @u is returned ++ */ ++struct kdbus_user *kdbus_user_ref(struct kdbus_user *u) ++{ ++ if (u) ++ kref_get(&u->kref); ++ return u; ++} ++ ++/** ++ * kdbus_user_unref() - drop a user reference ++ * @u: User ++ * ++ * Return: NULL ++ */ ++struct kdbus_user *kdbus_user_unref(struct kdbus_user *u) ++{ ++ if (u) ++ kref_put(&u->kref, __kdbus_user_free); ++ return NULL; ++} +diff --git a/ipc/kdbus/domain.h b/ipc/kdbus/domain.h +new file mode 100644 +index 000000000000..447a2bd4d972 +--- /dev/null ++++ b/ipc/kdbus/domain.h +@@ -0,0 +1,77 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> ++ * ++ * 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/fs.h> ++#include <linux/idr.h> ++#include <linux/kref.h> ++#include <linux/user_namespace.h> ++ ++#include "node.h" ++ ++/** ++ * struct kdbus_domain - domain for buses ++ * @node: Underlying API node ++ * @lock: Domain data lock ++ * @last_id: Last used object id ++ * @user_idr: Set of all users indexed by UID ++ * @user_ida: Set of all users to compute small indices ++ * @user_namespace: User namespace, pinned at creation time ++ * @dentry: Root dentry of VFS mount (don't use outside of kdbusfs) ++ */ ++struct kdbus_domain { ++ struct kdbus_node node; ++ struct mutex lock; ++ atomic64_t last_id; ++ struct idr user_idr; ++ struct ida user_ida; ++ struct user_namespace *user_namespace; ++ struct dentry *dentry; ++}; ++ ++/** ++ * struct kdbus_user - resource accounting for users ++ * @kref: Reference counter ++ * @domain: Domain of the user ++ * @id: Index of this user ++ * @uid: UID of the user ++ * @buses: Number of buses the user has created ++ * @connections: Number of connections the user has created ++ */ ++struct kdbus_user { ++ struct kref kref; ++ struct kdbus_domain *domain; ++ unsigned int id; ++ kuid_t uid; ++ atomic_t buses; ++ atomic_t connections; ++}; ++ ++#define kdbus_domain_from_node(_node) \ ++ container_of((_node), struct kdbus_domain, node) ++ ++struct kdbus_domain *kdbus_domain_new(unsigned int access); ++struct kdbus_domain *kdbus_domain_ref(struct kdbus_domain *domain); ++struct kdbus_domain *kdbus_domain_unref(struct kdbus_domain *domain); ++int kdbus_domain_populate(struct kdbus_domain *domain, unsigned int access); ++ ++#define KDBUS_USER_KERNEL_ID 0 /* ID 0 is reserved for kernel accounting */ ++ ++struct kdbus_user *kdbus_user_lookup(struct kdbus_domain *domain, kuid_t uid); ++struct kdbus_user *kdbus_user_ref(struct kdbus_user *u); ++struct kdbus_user *kdbus_user_unref(struct kdbus_user *u); ++ ++#endif +diff --git a/ipc/kdbus/endpoint.c b/ipc/kdbus/endpoint.c +new file mode 100644 +index 000000000000..174d274b113e +--- /dev/null ++++ b/ipc/kdbus/endpoint.c +@@ -0,0 +1,275 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> ++ * ++ * 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/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 <linux/uio.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" ++ ++static void kdbus_ep_free(struct kdbus_node *node) ++{ ++ struct kdbus_ep *ep = container_of(node, struct kdbus_ep, node); ++ ++ WARN_ON(!list_empty(&ep->conn_list)); ++ ++ kdbus_policy_db_clear(&ep->policy_db); ++ kdbus_bus_unref(ep->bus); ++ kdbus_user_unref(ep->user); ++ kfree(ep); ++} ++ ++static void kdbus_ep_release(struct kdbus_node *node, bool was_active) ++{ ++ struct kdbus_ep *ep = container_of(node, struct kdbus_ep, node); ++ ++ /* 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); ++ } ++} ++ ++/** ++ * kdbus_ep_new() - create a new endpoint ++ * @bus: The bus this endpoint will be created for ++ * @name: The name of the endpoint ++ * @access: The access flags for this node (KDBUS_MAKE_ACCESS_*) ++ * @uid: The uid of the node ++ * @gid: The gid of the node ++ * @is_custom: Whether this is a custom endpoint ++ * ++ * This function will create a new enpoint with the given ++ * name and properties for a given bus. ++ * ++ * Return: a new kdbus_ep on success, ERR_PTR on failure. ++ */ ++struct kdbus_ep *kdbus_ep_new(struct kdbus_bus *bus, const char *name, ++ unsigned int access, kuid_t uid, kgid_t gid, ++ bool is_custom) ++{ ++ struct kdbus_ep *e; ++ int ret; ++ ++ /* ++ * Validate only custom endpoints names, default endpoints ++ * with a "bus" name are created when the bus is created ++ */ ++ if (is_custom) { ++ ret = kdbus_verify_uid_prefix(name, bus->domain->user_namespace, ++ uid); ++ if (ret < 0) ++ return ERR_PTR(ret); ++ } ++ ++ e = kzalloc(sizeof(*e), GFP_KERNEL); ++ if (!e) ++ return ERR_PTR(-ENOMEM); ++ ++ kdbus_node_init(&e->node, KDBUS_NODE_ENDPOINT); ++ ++ e->node.free_cb = kdbus_ep_free; ++ e->node.release_cb = kdbus_ep_release; ++ e->node.uid = uid; ++ e->node.gid = gid; ++ e->node.mode = S_IRUSR | S_IWUSR; ++ if (access & (KDBUS_MAKE_ACCESS_GROUP | KDBUS_MAKE_ACCESS_WORLD)) ++ e->node.mode |= S_IRGRP | S_IWGRP; ++ if (access & KDBUS_MAKE_ACCESS_WORLD) ++ e->node.mode |= S_IROTH | S_IWOTH; ++ ++ mutex_init(&e->lock); ++ INIT_LIST_HEAD(&e->conn_list); ++ kdbus_policy_db_init(&e->policy_db); ++ e->bus = kdbus_bus_ref(bus); ++ ++ ret = kdbus_node_link(&e->node, &bus->node, name); ++ if (ret < 0) ++ goto exit_unref; ++ ++ /* ++ * Transactions on custom endpoints are never accounted on the global ++ * user limits. Instead, for each custom endpoint, we create a custom, ++ * unique user, which all transactions are accounted on. Regardless of ++ * the user using that endpoint, it is always accounted on the same ++ * user-object. This budget is not shared with ordinary users on ++ * non-custom endpoints. ++ */ ++ if (is_custom) { ++ e->user = kdbus_user_lookup(bus->domain, INVALID_UID); ++ if (IS_ERR(e->user)) { ++ ret = PTR_ERR(e->user); ++ e->user = NULL; ++ goto exit_unref; ++ } ++ } ++ ++ return e; ++ ++exit_unref: ++ kdbus_node_deactivate(&e->node); ++ kdbus_node_unref(&e->node); ++ return ERR_PTR(ret); ++} ++ ++/** ++ * kdbus_ep_ref() - increase the reference counter of a kdbus_ep ++ * @ep: The endpoint to reference ++ * ++ * Every user of an endpoint, except for its creator, must add a reference to ++ * the kdbus_ep instance using this function. ++ * ++ * Return: the ep itself ++ */ ++struct kdbus_ep *kdbus_ep_ref(struct kdbus_ep *ep) ++{ ++ if (ep) ++ kdbus_node_ref(&ep->node); ++ return ep; ++} ++ ++/** ++ * kdbus_ep_unref() - decrease the reference counter of a kdbus_ep ++ * @ep: The ep to unref ++ * ++ * Release a reference. If the reference count drops to 0, the ep will be ++ * freed. ++ * ++ * Return: NULL ++ */ ++struct kdbus_ep *kdbus_ep_unref(struct kdbus_ep *ep) ++{ ++ if (ep) ++ kdbus_node_unref(&ep->node); ++ return NULL; ++} ++ ++/** ++ * kdbus_cmd_ep_make() - handle KDBUS_CMD_ENDPOINT_MAKE ++ * @bus: bus to operate on ++ * @argp: command payload ++ * ++ * Return: Newly created endpoint on success, ERR_PTR on failure. ++ */ ++struct kdbus_ep *kdbus_cmd_ep_make(struct kdbus_bus *bus, void __user *argp) ++{ ++ const char *item_make_name; ++ struct kdbus_ep *ep = NULL; ++ struct kdbus_cmd *cmd; ++ int ret; ++ ++ struct kdbus_arg argv[] = { ++ { .type = KDBUS_ITEM_NEGOTIATE }, ++ { .type = KDBUS_ITEM_MAKE_NAME, .mandatory = true }, ++ }; ++ struct kdbus_args args = { ++ .allowed_flags = KDBUS_FLAG_NEGOTIATE | ++ KDBUS_MAKE_ACCESS_GROUP | ++ KDBUS_MAKE_ACCESS_WORLD, ++ .argv = argv, ++ .argc = ARRAY_SIZE(argv), ++ }; ++ ++ ret = kdbus_args_parse(&args, argp, &cmd); ++ if (ret < 0) ++ return ERR_PTR(ret); ++ if (ret > 0) ++ return NULL; ++ ++ item_make_name = argv[1].item->str; ++ ++ ep = kdbus_ep_new(bus, item_make_name, cmd->flags, ++ current_euid(), current_egid(), true); ++ if (IS_ERR(ep)) { ++ ret = PTR_ERR(ep); ++ ep = NULL; ++ goto exit; ++ } ++ ++ if (!kdbus_node_activate(&ep->node)) { ++ ret = -ESHUTDOWN; ++ goto exit; ++ } ++ ++exit: ++ ret = kdbus_args_clear(&args, ret); ++ if (ret < 0) { ++ if (ep) { ++ kdbus_node_deactivate(&ep->node); ++ kdbus_ep_unref(ep); ++ } ++ return ERR_PTR(ret); ++ } ++ return ep; ++} ++ ++/** ++ * kdbus_cmd_ep_update() - handle KDBUS_CMD_ENDPOINT_UPDATE ++ * @ep: endpoint to operate on ++ * @argp: command payload ++ * ++ * Return: Newly created endpoint on success, ERR_PTR on failure. ++ */ ++int kdbus_cmd_ep_update(struct kdbus_ep *ep, void __user *argp) ++{ ++ struct kdbus_cmd *cmd; ++ int ret; ++ ++ struct kdbus_arg argv[] = { ++ { .type = KDBUS_ITEM_NEGOTIATE }, ++ { .type = KDBUS_ITEM_NAME, .multiple = true }, ++ { .type = KDBUS_ITEM_POLICY_ACCESS, .multiple = true }, ++ }; ++ struct kdbus_args args = { ++ .allowed_flags = KDBUS_FLAG_NEGOTIATE, ++ .argv = argv, ++ .argc = ARRAY_SIZE(argv), ++ }; ++ ++ ret = kdbus_args_parse(&args, argp, &cmd); ++ if (ret != 0) ++ return ret; ++ ++ ret = kdbus_policy_set(&ep->policy_db, args.items, args.items_size, ++ 0, true, ep); ++ return kdbus_args_clear(&args, ret); ++} +diff --git a/ipc/kdbus/endpoint.h b/ipc/kdbus/endpoint.h +new file mode 100644 +index 000000000000..d31954bfba2c +--- /dev/null ++++ b/ipc/kdbus/endpoint.h +@@ -0,0 +1,67 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> ++ * ++ * 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/list.h> ++#include <linux/mutex.h> ++#include <linux/uidgid.h> ++#include "node.h" ++#include "policy.h" ++ ++struct kdbus_bus; ++struct kdbus_user; ++ ++/** ++ * struct kdbus_ep - enpoint to access a bus ++ * @node: The kdbus node ++ * @lock: Endpoint data lock ++ * @bus: Bus behind this endpoint ++ * @user: Custom enpoints account against an anonymous user ++ * @policy_db: Uploaded policy ++ * @conn_list: Connections of this endpoint ++ * ++ * An enpoint offers access to a bus; the default endpoint 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 kdbus_node node; ++ struct mutex lock; ++ ++ /* static */ ++ struct kdbus_bus *bus; ++ struct kdbus_user *user; ++ ++ /* protected by own locks */ ++ struct kdbus_policy_db policy_db; ++ ++ /* protected by ep->lock */ ++ struct list_head conn_list; ++}; ++ ++#define kdbus_ep_from_node(_node) \ ++ container_of((_node), struct kdbus_ep, node) ++ ++struct kdbus_ep *kdbus_ep_new(struct kdbus_bus *bus, const char *name, ++ unsigned int access, kuid_t uid, kgid_t gid, ++ bool policy); ++struct kdbus_ep *kdbus_ep_ref(struct kdbus_ep *ep); ++struct kdbus_ep *kdbus_ep_unref(struct kdbus_ep *ep); ++ ++struct kdbus_ep *kdbus_cmd_ep_make(struct kdbus_bus *bus, void __user *argp); ++int kdbus_cmd_ep_update(struct kdbus_ep *ep, void __user *argp); ++ ++#endif diff --git a/kdbus-add-code-for-notifications-and-matches.patch b/kdbus-add-code-for-notifications-and-matches.patch new file mode 100644 index 000000000..b9ec77f50 --- /dev/null +++ b/kdbus-add-code-for-notifications-and-matches.patch @@ -0,0 +1,929 @@ +From: Daniel Mack <daniel@zonque.org> +Date: Thu, 11 Sep 2014 18:59:16 +0200 +Subject: [PATCH] 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: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Djalal Harouni <tixxdz@opendz.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + ipc/kdbus/match.c | 559 +++++++++++++++++++++++++++++++++++++++++++++++++++++ + ipc/kdbus/match.h | 35 ++++ + ipc/kdbus/notify.c | 248 ++++++++++++++++++++++++ + ipc/kdbus/notify.h | 30 +++ + 4 files changed, 872 insertions(+) + create mode 100644 ipc/kdbus/match.c + create mode 100644 ipc/kdbus/match.h + create mode 100644 ipc/kdbus/notify.c + create mode 100644 ipc/kdbus/notify.h + +diff --git a/ipc/kdbus/match.c b/ipc/kdbus/match.c +new file mode 100644 +index 000000000000..30cec1ca819f +--- /dev/null ++++ b/ipc/kdbus/match.c +@@ -0,0 +1,559 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#include <linux/fs.h> ++#include <linux/hash.h> ++#include <linux/init.h> ++#include <linux/mutex.h> ++#include <linux/sched.h> ++#include <linux/sizes.h> ++#include <linux/slab.h> ++#include <linux/uaccess.h> ++ ++#include "bus.h" ++#include "connection.h" ++#include "endpoint.h" ++#include "handle.h" ++#include "item.h" ++#include "match.h" ++#include "message.h" ++#include "names.h" ++ ++/** ++ * struct kdbus_match_db - message filters ++ * @entries_list: List of matches ++ * @mdb_rwlock: Match data lock ++ * @entries_count: Number of entries in database ++ */ ++struct kdbus_match_db { ++ struct list_head entries_list; ++ struct rw_semaphore mdb_rwlock; ++ unsigned int entries_count; ++}; ++ ++/** ++ * struct kdbus_match_entry - a match database entry ++ * @cookie: User-supplied cookie to lookup the entry ++ * @list_entry: The list entry element for the db list ++ * @rules_list: The list head for tracking rules of this entry ++ */ ++struct kdbus_match_entry { ++ u64 cookie; ++ struct list_head list_entry; ++ struct list_head rules_list; ++}; ++ ++/** ++ * struct kdbus_bloom_mask - mask to match against filter ++ * @generations: Number of generations carried ++ * @data: Array of bloom bit fields ++ */ ++struct kdbus_bloom_mask { ++ u64 generations; ++ u64 *data; ++}; ++ ++/** ++ * struct kdbus_match_rule - a rule appended to a match entry ++ * @type: An item type to match agains ++ * @bloom_mask: Bloom mask to match a message's filter against, used ++ * with KDBUS_ITEM_BLOOM_MASK ++ * @name: Name to match against, used with KDBUS_ITEM_NAME, ++ * KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE} ++ * @old_id: ID to match against, used with ++ * KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE}, ++ * KDBUS_ITEM_ID_REMOVE ++ * @new_id: ID to match against, used with ++ * KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE}, ++ * KDBUS_ITEM_ID_REMOVE ++ * @src_id: ID to match against, used with KDBUS_ITEM_ID ++ * @rules_entry: Entry in the entry's rules list ++ */ ++struct kdbus_match_rule { ++ u64 type; ++ union { ++ struct kdbus_bloom_mask bloom_mask; ++ struct { ++ char *name; ++ u64 old_id; ++ u64 new_id; ++ }; ++ u64 src_id; ++ }; ++ struct list_head rules_entry; ++}; ++ ++static void kdbus_match_rule_free(struct kdbus_match_rule *rule) ++{ ++ if (!rule) ++ return; ++ ++ switch (rule->type) { ++ case KDBUS_ITEM_BLOOM_MASK: ++ kfree(rule->bloom_mask.data); ++ break; ++ ++ case KDBUS_ITEM_NAME: ++ case KDBUS_ITEM_NAME_ADD: ++ case KDBUS_ITEM_NAME_REMOVE: ++ case KDBUS_ITEM_NAME_CHANGE: ++ kfree(rule->name); ++ break; ++ ++ case KDBUS_ITEM_ID: ++ case KDBUS_ITEM_ID_ADD: ++ case KDBUS_ITEM_ID_REMOVE: ++ break; ++ ++ default: ++ BUG(); ++ } ++ ++ list_del(&rule->rules_entry); ++ kfree(rule); ++} ++ ++static void kdbus_match_entry_free(struct kdbus_match_entry *entry) ++{ ++ struct kdbus_match_rule *r, *tmp; ++ ++ if (!entry) ++ return; ++ ++ list_for_each_entry_safe(r, tmp, &entry->rules_list, rules_entry) ++ kdbus_match_rule_free(r); ++ ++ list_del(&entry->list_entry); ++ kfree(entry); ++} ++ ++/** ++ * kdbus_match_db_free() - free match db resources ++ * @mdb: The match database ++ */ ++void kdbus_match_db_free(struct kdbus_match_db *mdb) ++{ ++ struct kdbus_match_entry *entry, *tmp; ++ ++ if (!mdb) ++ return; ++ ++ list_for_each_entry_safe(entry, tmp, &mdb->entries_list, list_entry) ++ kdbus_match_entry_free(entry); ++ ++ kfree(mdb); ++} ++ ++/** ++ * kdbus_match_db_new() - create a new match database ++ * ++ * Return: a new kdbus_match_db on success, ERR_PTR on failure. ++ */ ++struct kdbus_match_db *kdbus_match_db_new(void) ++{ ++ struct kdbus_match_db *d; ++ ++ d = kzalloc(sizeof(*d), GFP_KERNEL); ++ if (!d) ++ return ERR_PTR(-ENOMEM); ++ ++ init_rwsem(&d->mdb_rwlock); ++ INIT_LIST_HEAD(&d->entries_list); ++ ++ return d; ++} ++ ++static bool kdbus_match_bloom(const struct kdbus_bloom_filter *filter, ++ const struct kdbus_bloom_mask *mask, ++ const struct kdbus_conn *conn) ++{ ++ size_t n = conn->ep->bus->bloom.size / sizeof(u64); ++ const u64 *m; ++ size_t i; ++ ++ /* ++ * The message's filter carries a generation identifier, the ++ * match's mask possibly carries an array of multiple generations ++ * of the mask. Select the mask with the closest match of the ++ * filter's generation. ++ */ ++ m = mask->data + (min(filter->generation, mask->generations - 1) * n); ++ ++ /* ++ * The message's filter contains the messages properties, ++ * the match's mask contains the properties to look for in the ++ * message. Check the mask bit field against the filter bit field, ++ * if the message possibly carries the properties the connection ++ * has subscribed to. ++ */ ++ for (i = 0; i < n; i++) ++ if ((filter->data[i] & m[i]) != m[i]) ++ return false; ++ ++ return true; ++} ++ ++static bool kdbus_match_rules(const struct kdbus_match_entry *entry, ++ struct kdbus_conn *conn_src, ++ struct kdbus_kmsg *kmsg) ++{ ++ struct kdbus_match_rule *r; ++ ++ if (conn_src) ++ lockdep_assert_held(&conn_src->ep->bus->name_registry->rwlock); ++ ++ /* ++ * Walk all the rules and bail out immediately ++ * if any of them is unsatisfied. ++ */ ++ ++ list_for_each_entry(r, &entry->rules_list, rules_entry) { ++ if (conn_src) { ++ /* messages from userspace */ ++ ++ switch (r->type) { ++ case KDBUS_ITEM_BLOOM_MASK: ++ if (!kdbus_match_bloom(kmsg->bloom_filter, ++ &r->bloom_mask, ++ conn_src)) ++ return false; ++ break; ++ ++ case KDBUS_ITEM_ID: ++ if (r->src_id != conn_src->id && ++ r->src_id != KDBUS_MATCH_ID_ANY) ++ return false; ++ ++ break; ++ ++ case KDBUS_ITEM_NAME: ++ if (!kdbus_conn_has_name(conn_src, r->name)) ++ return false; ++ ++ break; ++ ++ default: ++ return false; ++ } ++ } else { ++ /* kernel notifications */ ++ ++ if (kmsg->notify_type != r->type) ++ return false; ++ ++ switch (r->type) { ++ case KDBUS_ITEM_ID_ADD: ++ if (r->new_id != KDBUS_MATCH_ID_ANY && ++ r->new_id != kmsg->notify_new_id) ++ return false; ++ ++ break; ++ ++ case KDBUS_ITEM_ID_REMOVE: ++ if (r->old_id != KDBUS_MATCH_ID_ANY && ++ r->old_id != kmsg->notify_old_id) ++ return false; ++ ++ break; ++ ++ case KDBUS_ITEM_NAME_ADD: ++ case KDBUS_ITEM_NAME_CHANGE: ++ case KDBUS_ITEM_NAME_REMOVE: ++ if ((r->old_id != KDBUS_MATCH_ID_ANY && ++ r->old_id != kmsg->notify_old_id) || ++ (r->new_id != KDBUS_MATCH_ID_ANY && ++ r->new_id != kmsg->notify_new_id) || ++ (r->name && kmsg->notify_name && ++ strcmp(r->name, kmsg->notify_name) != 0)) ++ return false; ++ ++ break; ++ ++ default: ++ return false; ++ } ++ } ++ } ++ ++ return true; ++} ++ ++/** ++ * kdbus_match_db_match_kmsg() - match a kmsg object agains the database entries ++ * @mdb: The match database ++ * @conn_src: The connection object originating the message ++ * @kmsg: The kmsg to perform the match on ++ * ++ * This function will walk through all the database entries previously uploaded ++ * with kdbus_match_db_add(). As soon as any of them has an all-satisfied rule ++ * set, this function will return true. ++ * ++ * The caller must hold the registry lock of conn_src->ep->bus, in case conn_src ++ * is non-NULL. ++ * ++ * Return: true if there was a matching database entry, false otherwise. ++ */ ++bool kdbus_match_db_match_kmsg(struct kdbus_match_db *mdb, ++ struct kdbus_conn *conn_src, ++ struct kdbus_kmsg *kmsg) ++{ ++ struct kdbus_match_entry *entry; ++ bool matched = false; ++ ++ down_read(&mdb->mdb_rwlock); ++ list_for_each_entry(entry, &mdb->entries_list, list_entry) { ++ matched = kdbus_match_rules(entry, conn_src, kmsg); ++ if (matched) ++ break; ++ } ++ up_read(&mdb->mdb_rwlock); ++ ++ return matched; ++} ++ ++static int kdbus_match_db_remove_unlocked(struct kdbus_match_db *mdb, ++ u64 cookie) ++{ ++ struct kdbus_match_entry *entry, *tmp; ++ bool found = false; ++ ++ list_for_each_entry_safe(entry, tmp, &mdb->entries_list, list_entry) ++ if (entry->cookie == cookie) { ++ kdbus_match_entry_free(entry); ++ --mdb->entries_count; ++ found = true; ++ } ++ ++ return found ? 0 : -EBADSLT; ++} ++ ++/** ++ * kdbus_cmd_match_add() - handle KDBUS_CMD_MATCH_ADD ++ * @conn: connection to operate on ++ * @argp: command payload ++ * ++ * One call to this function (or one ioctl(KDBUS_CMD_MATCH_ADD), respectively, ++ * adds one new database entry with n rules attached to it. Each rule is ++ * described with an kdbus_item, and an entry is considered matching if all ++ * its rules are satisfied. ++ * ++ * The items attached to a kdbus_cmd_match struct have the following mapping: ++ * ++ * KDBUS_ITEM_BLOOM_MASK: A bloom mask ++ * KDBUS_ITEM_NAME: A connection's source name ++ * KDBUS_ITEM_ID: A connection ID ++ * KDBUS_ITEM_NAME_ADD: ++ * KDBUS_ITEM_NAME_REMOVE: ++ * KDBUS_ITEM_NAME_CHANGE: Well-known name changes, carry ++ * kdbus_notify_name_change ++ * KDBUS_ITEM_ID_ADD: ++ * KDBUS_ITEM_ID_REMOVE: Connection ID changes, carry ++ * kdbus_notify_id_change ++ * ++ * For kdbus_notify_{id,name}_change structs, only the ID and name fields ++ * are looked at when adding an entry. The flags are unused. ++ * ++ * Also note that KDBUS_ITEM_BLOOM_MASK, KDBUS_ITEM_NAME and KDBUS_ITEM_ID ++ * are used to match messages from userspace, while the others apply to ++ * kernel-generated notifications. ++ * ++ * Return: 0 on success, negative error code on failure. ++ */ ++int kdbus_cmd_match_add(struct kdbus_conn *conn, void __user *argp) ++{ ++ struct kdbus_match_db *mdb = conn->match_db; ++ struct kdbus_match_entry *entry = NULL; ++ struct kdbus_cmd_match *cmd; ++ struct kdbus_item *item; ++ int ret; ++ ++ struct kdbus_arg argv[] = { ++ { .type = KDBUS_ITEM_NEGOTIATE }, ++ { .type = KDBUS_ITEM_BLOOM_MASK, .multiple = true }, ++ { .type = KDBUS_ITEM_NAME, .multiple = true }, ++ { .type = KDBUS_ITEM_ID, .multiple = true }, ++ { .type = KDBUS_ITEM_NAME_ADD, .multiple = true }, ++ { .type = KDBUS_ITEM_NAME_REMOVE, .multiple = true }, ++ { .type = KDBUS_ITEM_NAME_CHANGE, .multiple = true }, ++ { .type = KDBUS_ITEM_ID_ADD, .multiple = true }, ++ { .type = KDBUS_ITEM_ID_REMOVE, .multiple = true }, ++ }; ++ struct kdbus_args args = { ++ .allowed_flags = KDBUS_FLAG_NEGOTIATE | ++ KDBUS_MATCH_REPLACE, ++ .argv = argv, ++ .argc = ARRAY_SIZE(argv), ++ }; ++ ++ if (!kdbus_conn_is_ordinary(conn)) ++ return -EOPNOTSUPP; ++ ++ ret = kdbus_args_parse(&args, argp, &cmd); ++ if (ret != 0) ++ return ret; ++ ++ entry = kzalloc(sizeof(*entry), GFP_KERNEL); ++ if (!entry) { ++ ret = -ENOMEM; ++ goto exit; ++ } ++ ++ entry->cookie = cmd->cookie; ++ INIT_LIST_HEAD(&entry->list_entry); ++ INIT_LIST_HEAD(&entry->rules_list); ++ ++ KDBUS_ITEMS_FOREACH(item, cmd->items, KDBUS_ITEMS_SIZE(cmd, items)) { ++ struct kdbus_match_rule *rule; ++ size_t size = item->size - offsetof(struct kdbus_item, data); ++ ++ rule = kzalloc(sizeof(*rule), GFP_KERNEL); ++ if (!rule) { ++ ret = -ENOMEM; ++ goto exit; ++ } ++ ++ rule->type = item->type; ++ INIT_LIST_HEAD(&rule->rules_entry); ++ ++ switch (item->type) { ++ case KDBUS_ITEM_BLOOM_MASK: { ++ u64 bsize = conn->ep->bus->bloom.size; ++ u64 generations; ++ u64 remainder; ++ ++ generations = div64_u64_rem(size, bsize, &remainder); ++ if (size < bsize || remainder > 0) { ++ ret = -EDOM; ++ break; ++ } ++ ++ rule->bloom_mask.data = kmemdup(item->data, ++ size, GFP_KERNEL); ++ if (!rule->bloom_mask.data) { ++ ret = -ENOMEM; ++ break; ++ } ++ ++ rule->bloom_mask.generations = generations; ++ break; ++ } ++ ++ case KDBUS_ITEM_NAME: ++ if (!kdbus_name_is_valid(item->str, false)) { ++ ret = -EINVAL; ++ break; ++ } ++ ++ rule->name = kstrdup(item->str, GFP_KERNEL); ++ if (!rule->name) ++ ret = -ENOMEM; ++ ++ break; ++ ++ case KDBUS_ITEM_ID: ++ rule->src_id = item->id; ++ break; ++ ++ case KDBUS_ITEM_NAME_ADD: ++ case KDBUS_ITEM_NAME_REMOVE: ++ case KDBUS_ITEM_NAME_CHANGE: ++ rule->old_id = item->name_change.old_id.id; ++ rule->new_id = item->name_change.new_id.id; ++ ++ if (size > sizeof(struct kdbus_notify_name_change)) { ++ rule->name = kstrdup(item->name_change.name, ++ GFP_KERNEL); ++ if (!rule->name) ++ ret = -ENOMEM; ++ } ++ ++ break; ++ ++ case KDBUS_ITEM_ID_ADD: ++ case KDBUS_ITEM_ID_REMOVE: ++ if (item->type == KDBUS_ITEM_ID_ADD) ++ rule->new_id = item->id_change.id; ++ else ++ rule->old_id = item->id_change.id; ++ ++ break; ++ } ++ ++ if (ret < 0) { ++ kdbus_match_rule_free(rule); ++ goto exit; ++ } ++ ++ list_add_tail(&rule->rules_entry, &entry->rules_list); ++ } ++ ++ down_write(&mdb->mdb_rwlock); ++ ++ /* Remove any entry that has the same cookie as the current one. */ ++ if (cmd->flags & KDBUS_MATCH_REPLACE) ++ kdbus_match_db_remove_unlocked(mdb, entry->cookie); ++ ++ /* ++ * If the above removal caught any entry, there will be room for the ++ * new one. ++ */ ++ if (++mdb->entries_count > KDBUS_MATCH_MAX) { ++ --mdb->entries_count; ++ ret = -EMFILE; ++ } else { ++ list_add_tail(&entry->list_entry, &mdb->entries_list); ++ entry = NULL; ++ } ++ ++ up_write(&mdb->mdb_rwlock); ++ ++exit: ++ kdbus_match_entry_free(entry); ++ return kdbus_args_clear(&args, ret); ++} ++ ++/** ++ * kdbus_cmd_match_remove() - handle KDBUS_CMD_MATCH_REMOVE ++ * @conn: connection to operate on ++ * @argp: command payload ++ * ++ * Return: 0 on success, negative error code on failure. ++ */ ++int kdbus_cmd_match_remove(struct kdbus_conn *conn, void __user *argp) ++{ ++ struct kdbus_cmd_match *cmd; ++ int ret; ++ ++ struct kdbus_arg argv[] = { ++ { .type = KDBUS_ITEM_NEGOTIATE }, ++ }; ++ struct kdbus_args args = { ++ .allowed_flags = KDBUS_FLAG_NEGOTIATE, ++ .argv = argv, ++ .argc = ARRAY_SIZE(argv), ++ }; ++ ++ if (!kdbus_conn_is_ordinary(conn)) ++ return -EOPNOTSUPP; ++ ++ ret = kdbus_args_parse(&args, argp, &cmd); ++ if (ret != 0) ++ return ret; ++ ++ down_write(&conn->match_db->mdb_rwlock); ++ ret = kdbus_match_db_remove_unlocked(conn->match_db, cmd->cookie); ++ up_write(&conn->match_db->mdb_rwlock); ++ ++ return kdbus_args_clear(&args, ret); ++} +diff --git a/ipc/kdbus/match.h b/ipc/kdbus/match.h +new file mode 100644 +index 000000000000..ea4292938deb +--- /dev/null ++++ b/ipc/kdbus/match.h +@@ -0,0 +1,35 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#ifndef __KDBUS_MATCH_H ++#define __KDBUS_MATCH_H ++ ++struct kdbus_conn; ++struct kdbus_kmsg; ++struct kdbus_match_db; ++ ++struct kdbus_match_db *kdbus_match_db_new(void); ++void kdbus_match_db_free(struct kdbus_match_db *db); ++int kdbus_match_db_add(struct kdbus_conn *conn, ++ struct kdbus_cmd_match *cmd); ++int kdbus_match_db_remove(struct kdbus_conn *conn, ++ struct kdbus_cmd_match *cmd); ++bool kdbus_match_db_match_kmsg(struct kdbus_match_db *db, ++ struct kdbus_conn *conn_src, ++ struct kdbus_kmsg *kmsg); ++ ++int kdbus_cmd_match_add(struct kdbus_conn *conn, void __user *argp); ++int kdbus_cmd_match_remove(struct kdbus_conn *conn, void __user *argp); ++ ++#endif +diff --git a/ipc/kdbus/notify.c b/ipc/kdbus/notify.c +new file mode 100644 +index 000000000000..e4a454222f09 +--- /dev/null ++++ b/ipc/kdbus/notify.c +@@ -0,0 +1,248 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> ++ * ++ * 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/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 "domain.h" ++#include "endpoint.h" ++#include "item.h" ++#include "message.h" ++#include "notify.h" ++ ++static inline void kdbus_notify_add_tail(struct kdbus_kmsg *kmsg, ++ struct kdbus_bus *bus) ++{ ++ spin_lock(&bus->notify_lock); ++ list_add_tail(&kmsg->notify_entry, &bus->notify_list); ++ spin_unlock(&bus->notify_lock); ++} ++ ++static int kdbus_notify_reply(struct kdbus_bus *bus, u64 id, ++ u64 cookie, u64 msg_type) ++{ ++ struct kdbus_kmsg *kmsg = NULL; ++ ++ WARN_ON(id == 0); ++ ++ kmsg = kdbus_kmsg_new(bus, 0); ++ if (IS_ERR(kmsg)) ++ return PTR_ERR(kmsg); ++ ++ /* ++ * 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.flags = KDBUS_MSG_SIGNAL; ++ 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; ++ ++ kdbus_notify_add_tail(kmsg, bus); ++ ++ return 0; ++} ++ ++/** ++ * 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; ++ ++ name_len = strlen(name) + 1; ++ extra_size = sizeof(struct kdbus_notify_name_change) + name_len; ++ kmsg = kdbus_kmsg_new(bus, extra_size); ++ if (IS_ERR(kmsg)) ++ return PTR_ERR(kmsg); ++ ++ kmsg->msg.flags = KDBUS_MSG_SIGNAL; ++ 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; ++ ++ kdbus_notify_add_tail(kmsg, bus); ++ ++ return 0; ++} ++ ++/** ++ * 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; ++ ++ kmsg = kdbus_kmsg_new(bus, sizeof(struct kdbus_notify_id_change)); ++ if (IS_ERR(kmsg)) ++ return PTR_ERR(kmsg); ++ ++ kmsg->msg.flags = KDBUS_MSG_SIGNAL; ++ 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; ++ ++ kdbus_notify_add_tail(kmsg, bus); ++ ++ return 0; ++} ++ ++/** ++ * 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; ++ ++ mutex_lock(&bus->notify_flush_lock); ++ down_read(&bus->name_registry->rwlock); ++ ++ 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, notify_entry) { ++ kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, NULL, ++ KDBUS_ATTACH_TIMESTAMP); ++ ++ if (kmsg->msg.dst_id != KDBUS_DST_ID_BROADCAST) { ++ struct kdbus_conn *conn; ++ ++ conn = kdbus_bus_find_conn_by_id(bus, kmsg->msg.dst_id); ++ if (conn) { ++ kdbus_bus_eavesdrop(bus, NULL, kmsg); ++ kdbus_conn_entry_insert(NULL, conn, kmsg, NULL); ++ kdbus_conn_unref(conn); ++ } ++ } else { ++ kdbus_bus_broadcast(bus, NULL, kmsg); ++ } ++ ++ list_del(&kmsg->notify_entry); ++ kdbus_kmsg_free(kmsg); ++ } ++ ++ up_read(&bus->name_registry->rwlock); ++ mutex_unlock(&bus->notify_flush_lock); ++} ++ ++/** ++ * 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, notify_entry) { ++ list_del(&kmsg->notify_entry); ++ kdbus_kmsg_free(kmsg); ++ } ++} +diff --git a/ipc/kdbus/notify.h b/ipc/kdbus/notify.h +new file mode 100644 +index 000000000000..03df464cb735 +--- /dev/null ++++ b/ipc/kdbus/notify.h +@@ -0,0 +1,30 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> ++ * ++ * 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/kdbus-add-code-to-gather-metadata.patch b/kdbus-add-code-to-gather-metadata.patch new file mode 100644 index 000000000..bd0bd9451 --- /dev/null +++ b/kdbus-add-code-to-gather-metadata.patch @@ -0,0 +1,1320 @@ +From: Daniel Mack <daniel@zonque.org> +Date: Thu, 11 Sep 2014 18:58:45 +0200 +Subject: [PATCH] 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. + +In traditional D-Bus, userspace tasks like polkit or journald make a +live lookup in procfs and sysfs to gain information about a sending +task. This is racy, of course, as in a a connection-less system like +D-Bus, the originating peer can go away immediately after sending the +message. As we're moving D-Bus prmitives into the kernel, we have to +provide the same semantics here, and inform the receiving peer on the +live credentials of the sending peer. + +Metadata is collected at the following times. + + * When a bus is created (KDBUS_CMD_MAKE), information about the + calling task is collected. This data is returned by the kernel + via the KDBUS_CMD_BUS_CREATOR_INFO call. + + * When a connection is created (KDBUS_CMD_HELLO), information about + the calling task is collected. Alternatively, a privileged + connection may provide 'faked' information about credentials, + PIDs and security labels which will be stored instead. This data + is returned by the kernel as information on a connection + (KDBUS_CMD_CONN_INFO). Only metadata that a connection allowed to + be sent (by setting its bit in attach_flags_send) will be exported + in this way. + + * When a message is sent (KDBUS_CMD_SEND), information about the + sending task and the sending connection are collected. This + metadata will be attached to the message when it arrives in the + receiver's pool. If the connection sending the message installed + faked credentials (see kdbus.connection(7)), the message will not + be augmented by any information about the currently sending task. + +Which metadata items are actually delivered depends on the following +sets and masks: + + (a) the system-wide kmod creds mask + (module parameter 'attach_flags_mask') + + (b) the per-connection send creds mask, set by the connecting client + + (c) the per-connection receive creds mask, set by the connecting client + + (d) the per-bus minimal creds mask, set by the bus creator + + (e) the per-bus owner creds mask, set by the bus creator + + (f) the mask specified when querying creds of a bus peer + + (g) the mask specified when querying creds of a bus owner + +With the following rules: + + [1] The creds attached to messages are determined as a & b & c. + + [2] When connecting to a bus (KDBUS_CMD_HELLO), and ~b & d != 0, + the call will fail with, -1, and errno is set to ECONNREFUSED. + + [3] When querying creds of a bus peer, the creds returned + are a & b & f. + + [4] When querying creds of a bus owner, the creds returned + are a & e & g. + +See kdbus.metadata(7) and kdbus.item(7) for more details on which +metadata can currently be attached to messages. + +Signed-off-by: Daniel Mack <daniel@zonque.org> +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Djalal Harouni <tixxdz@opendz.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + ipc/kdbus/metadata.c | 1164 ++++++++++++++++++++++++++++++++++++++++++++++++++ + ipc/kdbus/metadata.h | 57 +++ + 2 files changed, 1221 insertions(+) + create mode 100644 ipc/kdbus/metadata.c + create mode 100644 ipc/kdbus/metadata.h + +diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c +new file mode 100644 +index 000000000000..06e0a54a276a +--- /dev/null ++++ b/ipc/kdbus/metadata.c +@@ -0,0 +1,1164 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#include <linux/audit.h> ++#include <linux/capability.h> ++#include <linux/cgroup.h> ++#include <linux/cred.h> ++#include <linux/file.h> ++#include <linux/fs_struct.h> ++#include <linux/init.h> ++#include <linux/kref.h> ++#include <linux/mutex.h> ++#include <linux/sched.h> ++#include <linux/security.h> ++#include <linux/sizes.h> ++#include <linux/slab.h> ++#include <linux/uaccess.h> ++#include <linux/uidgid.h> ++#include <linux/uio.h> ++#include <linux/user_namespace.h> ++#include <linux/version.h> ++ ++#include "bus.h" ++#include "connection.h" ++#include "endpoint.h" ++#include "item.h" ++#include "message.h" ++#include "metadata.h" ++#include "names.h" ++ ++/** ++ * struct kdbus_meta_proc - Process metadata ++ * @kref: Reference counting ++ * @lock: Object lock ++ * @collected: Bitmask of collected items ++ * @valid: Bitmask of collected and valid items ++ * @uid: UID of process ++ * @euid: EUID of process ++ * @suid: SUID of process ++ * @fsuid: FSUID of process ++ * @gid: GID of process ++ * @egid: EGID of process ++ * @sgid: SGID of process ++ * @fsgid: FSGID of process ++ * @pid: PID of process ++ * @tgid: TGID of process ++ * @ppid: PPID of process ++ * @auxgrps: Auxiliary groups ++ * @n_auxgrps: Number of items in @auxgrps ++ * @tid_comm: TID comm line ++ * @pid_comm: PID comm line ++ * @exe_path: Executable path ++ * @root_path: Root-FS path ++ * @cmdline: Command-line ++ * @cgroup: Full cgroup path ++ * @caps: Capabilities ++ * @caps_namespace: User-namespace of @caps ++ * @seclabel: Seclabel ++ * @audit_loginuid: Audit login-UID ++ * @audit_sessionid: Audit session-ID ++ */ ++struct kdbus_meta_proc { ++ struct kref kref; ++ struct mutex lock; ++ u64 collected; ++ u64 valid; ++ ++ /* KDBUS_ITEM_CREDS */ ++ kuid_t uid, euid, suid, fsuid; ++ kgid_t gid, egid, sgid, fsgid; ++ ++ /* KDBUS_ITEM_PIDS */ ++ struct pid *pid; ++ struct pid *tgid; ++ struct pid *ppid; ++ ++ /* KDBUS_ITEM_AUXGROUPS */ ++ kgid_t *auxgrps; ++ size_t n_auxgrps; ++ ++ /* KDBUS_ITEM_TID_COMM */ ++ char tid_comm[TASK_COMM_LEN]; ++ /* KDBUS_ITEM_PID_COMM */ ++ char pid_comm[TASK_COMM_LEN]; ++ ++ /* KDBUS_ITEM_EXE */ ++ struct path exe_path; ++ struct path root_path; ++ ++ /* KDBUS_ITEM_CMDLINE */ ++ char *cmdline; ++ ++ /* KDBUS_ITEM_CGROUP */ ++ char *cgroup; ++ ++ /* KDBUS_ITEM_CAPS */ ++ struct caps { ++ /* binary compatible to kdbus_caps */ ++ u32 last_cap; ++ struct { ++ u32 caps[_KERNEL_CAPABILITY_U32S]; ++ } set[4]; ++ } caps; ++ struct user_namespace *caps_namespace; ++ ++ /* KDBUS_ITEM_SECLABEL */ ++ char *seclabel; ++ ++ /* KDBUS_ITEM_AUDIT */ ++ kuid_t audit_loginuid; ++ unsigned int audit_sessionid; ++}; ++ ++/** ++ * struct kdbus_meta_conn ++ * @kref: Reference counting ++ * @lock: Object lock ++ * @collected: Bitmask of collected items ++ * @valid: Bitmask of collected and valid items ++ * @ts: Timestamp values ++ * @owned_names_items: Serialized items for owned names ++ * @owned_names_size: Size of @owned_names_items ++ * @conn_description: Connection description ++ */ ++struct kdbus_meta_conn { ++ struct kref kref; ++ struct mutex lock; ++ u64 collected; ++ u64 valid; ++ ++ /* KDBUS_ITEM_TIMESTAMP */ ++ struct kdbus_timestamp ts; ++ ++ /* KDBUS_ITEM_OWNED_NAME */ ++ struct kdbus_item *owned_names_items; ++ size_t owned_names_size; ++ ++ /* KDBUS_ITEM_CONN_DESCRIPTION */ ++ char *conn_description; ++}; ++ ++/** ++ * kdbus_meta_proc_new() - Create process metadata object ++ * ++ * Return: Pointer to new object on success, ERR_PTR on failure. ++ */ ++struct kdbus_meta_proc *kdbus_meta_proc_new(void) ++{ ++ struct kdbus_meta_proc *mp; ++ ++ mp = kzalloc(sizeof(*mp), GFP_KERNEL); ++ if (!mp) ++ return ERR_PTR(-ENOMEM); ++ ++ kref_init(&mp->kref); ++ mutex_init(&mp->lock); ++ ++ return mp; ++} ++ ++static void kdbus_meta_proc_free(struct kref *kref) ++{ ++ struct kdbus_meta_proc *mp = container_of(kref, struct kdbus_meta_proc, ++ kref); ++ ++ path_put(&mp->exe_path); ++ path_put(&mp->root_path); ++ put_user_ns(mp->caps_namespace); ++ put_pid(mp->ppid); ++ put_pid(mp->tgid); ++ put_pid(mp->pid); ++ ++ kfree(mp->seclabel); ++ kfree(mp->auxgrps); ++ kfree(mp->cmdline); ++ kfree(mp->cgroup); ++ kfree(mp); ++} ++ ++/** ++ * kdbus_meta_proc_ref() - Gain reference ++ * @mp: Process metadata object ++ * ++ * Return: @mp is returned ++ */ ++struct kdbus_meta_proc *kdbus_meta_proc_ref(struct kdbus_meta_proc *mp) ++{ ++ if (mp) ++ kref_get(&mp->kref); ++ return mp; ++} ++ ++/** ++ * kdbus_meta_proc_unref() - Drop reference ++ * @mp: Process metadata object ++ * ++ * Return: NULL ++ */ ++struct kdbus_meta_proc *kdbus_meta_proc_unref(struct kdbus_meta_proc *mp) ++{ ++ if (mp) ++ kref_put(&mp->kref, kdbus_meta_proc_free); ++ return NULL; ++} ++ ++static void kdbus_meta_proc_collect_creds(struct kdbus_meta_proc *mp) ++{ ++ mp->uid = current_uid(); ++ mp->euid = current_euid(); ++ mp->suid = current_suid(); ++ mp->fsuid = current_fsuid(); ++ ++ mp->gid = current_gid(); ++ mp->egid = current_egid(); ++ mp->sgid = current_sgid(); ++ mp->fsgid = current_fsgid(); ++ ++ mp->valid |= KDBUS_ATTACH_CREDS; ++} ++ ++static void kdbus_meta_proc_collect_pids(struct kdbus_meta_proc *mp) ++{ ++ struct task_struct *parent; ++ ++ mp->pid = get_pid(task_pid(current)); ++ mp->tgid = get_pid(task_tgid(current)); ++ ++ rcu_read_lock(); ++ parent = rcu_dereference(current->real_parent); ++ mp->ppid = get_pid(task_tgid(parent)); ++ rcu_read_unlock(); ++ ++ mp->valid |= KDBUS_ATTACH_PIDS; ++} ++ ++static int kdbus_meta_proc_collect_auxgroups(struct kdbus_meta_proc *mp) ++{ ++ struct group_info *info; ++ size_t i; ++ ++ info = get_current_groups(); ++ ++ if (info->ngroups > 0) { ++ mp->auxgrps = kmalloc_array(info->ngroups, sizeof(kgid_t), ++ GFP_KERNEL); ++ if (!mp->auxgrps) { ++ put_group_info(info); ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < info->ngroups; i++) ++ mp->auxgrps[i] = GROUP_AT(info, i); ++ } ++ ++ mp->n_auxgrps = info->ngroups; ++ put_group_info(info); ++ mp->valid |= KDBUS_ATTACH_AUXGROUPS; ++ ++ return 0; ++} ++ ++static void kdbus_meta_proc_collect_tid_comm(struct kdbus_meta_proc *mp) ++{ ++ get_task_comm(mp->tid_comm, current); ++ mp->valid |= KDBUS_ATTACH_TID_COMM; ++} ++ ++static void kdbus_meta_proc_collect_pid_comm(struct kdbus_meta_proc *mp) ++{ ++ get_task_comm(mp->pid_comm, current->group_leader); ++ mp->valid |= KDBUS_ATTACH_PID_COMM; ++} ++ ++static void kdbus_meta_proc_collect_exe(struct kdbus_meta_proc *mp) ++{ ++ struct mm_struct *mm; ++ ++ mm = get_task_mm(current); ++ if (!mm) ++ return; ++ ++ down_read(&mm->mmap_sem); ++ if (mm->exe_file) { ++ mp->exe_path = mm->exe_file->f_path; ++ path_get(&mp->exe_path); ++ get_fs_root(current->fs, &mp->root_path); ++ mp->valid |= KDBUS_ATTACH_EXE; ++ } ++ up_read(&mm->mmap_sem); ++ ++ mmput(mm); ++} ++ ++static int kdbus_meta_proc_collect_cmdline(struct kdbus_meta_proc *mp) ++{ ++ struct mm_struct *mm; ++ char *cmdline; ++ ++ mm = get_task_mm(current); ++ if (!mm) ++ return 0; ++ ++ if (!mm->arg_end) { ++ mmput(mm); ++ return 0; ++ } ++ ++ cmdline = strndup_user((const char __user *)mm->arg_start, ++ mm->arg_end - mm->arg_start); ++ mmput(mm); ++ ++ if (IS_ERR(cmdline)) ++ return PTR_ERR(cmdline); ++ ++ mp->cmdline = cmdline; ++ mp->valid |= KDBUS_ATTACH_CMDLINE; ++ ++ return 0; ++} ++ ++static int kdbus_meta_proc_collect_cgroup(struct kdbus_meta_proc *mp) ++{ ++#ifdef CONFIG_CGROUPS ++ void *page; ++ char *s; ++ ++ page = (void *)__get_free_page(GFP_TEMPORARY); ++ if (!page) ++ return -ENOMEM; ++ ++ s = task_cgroup_path(current, page, PAGE_SIZE); ++ if (s) { ++ mp->cgroup = kstrdup(s, GFP_KERNEL); ++ if (!mp->cgroup) { ++ free_page((unsigned long)page); ++ return -ENOMEM; ++ } ++ } ++ ++ free_page((unsigned long)page); ++ mp->valid |= KDBUS_ATTACH_CGROUP; ++#endif ++ ++ return 0; ++} ++ ++static void kdbus_meta_proc_collect_caps(struct kdbus_meta_proc *mp) ++{ ++ const struct cred *c = current_cred(); ++ int i; ++ ++ /* ABI: "last_cap" equals /proc/sys/kernel/cap_last_cap */ ++ mp->caps.last_cap = CAP_LAST_CAP; ++ mp->caps_namespace = get_user_ns(current_user_ns()); ++ ++ CAP_FOR_EACH_U32(i) { ++ mp->caps.set[0].caps[i] = c->cap_inheritable.cap[i]; ++ mp->caps.set[1].caps[i] = c->cap_permitted.cap[i]; ++ mp->caps.set[2].caps[i] = c->cap_effective.cap[i]; ++ mp->caps.set[3].caps[i] = c->cap_bset.cap[i]; ++ } ++ ++ /* clear unused bits */ ++ for (i = 0; i < 4; i++) ++ mp->caps.set[i].caps[CAP_TO_INDEX(CAP_LAST_CAP)] &= ++ CAP_LAST_U32_VALID_MASK; ++ ++ mp->valid |= KDBUS_ATTACH_CAPS; ++} ++ ++static int kdbus_meta_proc_collect_seclabel(struct kdbus_meta_proc *mp) ++{ ++#ifdef CONFIG_SECURITY ++ char *ctx = NULL; ++ u32 sid, len; ++ int ret; ++ ++ security_task_getsecid(current, &sid); ++ ret = security_secid_to_secctx(sid, &ctx, &len); ++ if (ret < 0) { ++ /* ++ * EOPNOTSUPP means no security module is active, ++ * lets skip adding the seclabel then. This effectively ++ * drops the SECLABEL item. ++ */ ++ return (ret == -EOPNOTSUPP) ? 0 : ret; ++ } ++ ++ mp->seclabel = kstrdup(ctx, GFP_KERNEL); ++ security_release_secctx(ctx, len); ++ if (!mp->seclabel) ++ return -ENOMEM; ++ ++ mp->valid |= KDBUS_ATTACH_SECLABEL; ++#endif ++ ++ return 0; ++} ++ ++static void kdbus_meta_proc_collect_audit(struct kdbus_meta_proc *mp) ++{ ++#ifdef CONFIG_AUDITSYSCALL ++ mp->audit_loginuid = audit_get_loginuid(current); ++ mp->audit_sessionid = audit_get_sessionid(current); ++ mp->valid |= KDBUS_ATTACH_AUDIT; ++#endif ++} ++ ++/** ++ * kdbus_meta_proc_collect() - Collect process metadata ++ * @mp: Process metadata object ++ * @what: Attach flags to collect ++ * ++ * This collects process metadata from current and saves it in @mp. ++ * ++ * Return: 0 on success, negative error code on failure. ++ */ ++int kdbus_meta_proc_collect(struct kdbus_meta_proc *mp, u64 what) ++{ ++ int ret; ++ ++ if (!mp || !(what & (KDBUS_ATTACH_CREDS | ++ KDBUS_ATTACH_PIDS | ++ KDBUS_ATTACH_AUXGROUPS | ++ KDBUS_ATTACH_TID_COMM | ++ KDBUS_ATTACH_PID_COMM | ++ KDBUS_ATTACH_EXE | ++ KDBUS_ATTACH_CMDLINE | ++ KDBUS_ATTACH_CGROUP | ++ KDBUS_ATTACH_CAPS | ++ KDBUS_ATTACH_SECLABEL | ++ KDBUS_ATTACH_AUDIT))) ++ return 0; ++ ++ mutex_lock(&mp->lock); ++ ++ if ((what & KDBUS_ATTACH_CREDS) && ++ !(mp->collected & KDBUS_ATTACH_CREDS)) { ++ kdbus_meta_proc_collect_creds(mp); ++ mp->collected |= KDBUS_ATTACH_CREDS; ++ } ++ ++ if ((what & KDBUS_ATTACH_PIDS) && ++ !(mp->collected & KDBUS_ATTACH_PIDS)) { ++ kdbus_meta_proc_collect_pids(mp); ++ mp->collected |= KDBUS_ATTACH_PIDS; ++ } ++ ++ if ((what & KDBUS_ATTACH_AUXGROUPS) && ++ !(mp->collected & KDBUS_ATTACH_AUXGROUPS)) { ++ ret = kdbus_meta_proc_collect_auxgroups(mp); ++ if (ret < 0) ++ goto exit_unlock; ++ mp->collected |= KDBUS_ATTACH_AUXGROUPS; ++ } ++ ++ if ((what & KDBUS_ATTACH_TID_COMM) && ++ !(mp->collected & KDBUS_ATTACH_TID_COMM)) { ++ kdbus_meta_proc_collect_tid_comm(mp); ++ mp->collected |= KDBUS_ATTACH_TID_COMM; ++ } ++ ++ if ((what & KDBUS_ATTACH_PID_COMM) && ++ !(mp->collected & KDBUS_ATTACH_PID_COMM)) { ++ kdbus_meta_proc_collect_pid_comm(mp); ++ mp->collected |= KDBUS_ATTACH_PID_COMM; ++ } ++ ++ if ((what & KDBUS_ATTACH_EXE) && ++ !(mp->collected & KDBUS_ATTACH_EXE)) { ++ kdbus_meta_proc_collect_exe(mp); ++ mp->collected |= KDBUS_ATTACH_EXE; ++ } ++ ++ if ((what & KDBUS_ATTACH_CMDLINE) && ++ !(mp->collected & KDBUS_ATTACH_CMDLINE)) { ++ ret = kdbus_meta_proc_collect_cmdline(mp); ++ if (ret < 0) ++ goto exit_unlock; ++ mp->collected |= KDBUS_ATTACH_CMDLINE; ++ } ++ ++ if ((what & KDBUS_ATTACH_CGROUP) && ++ !(mp->collected & KDBUS_ATTACH_CGROUP)) { ++ ret = kdbus_meta_proc_collect_cgroup(mp); ++ if (ret < 0) ++ goto exit_unlock; ++ mp->collected |= KDBUS_ATTACH_CGROUP; ++ } ++ ++ if ((what & KDBUS_ATTACH_CAPS) && ++ !(mp->collected & KDBUS_ATTACH_CAPS)) { ++ kdbus_meta_proc_collect_caps(mp); ++ mp->collected |= KDBUS_ATTACH_CAPS; ++ } ++ ++ if ((what & KDBUS_ATTACH_SECLABEL) && ++ !(mp->collected & KDBUS_ATTACH_SECLABEL)) { ++ ret = kdbus_meta_proc_collect_seclabel(mp); ++ if (ret < 0) ++ goto exit_unlock; ++ mp->collected |= KDBUS_ATTACH_SECLABEL; ++ } ++ ++ if ((what & KDBUS_ATTACH_AUDIT) && ++ !(mp->collected & KDBUS_ATTACH_AUDIT)) { ++ kdbus_meta_proc_collect_audit(mp); ++ mp->collected |= KDBUS_ATTACH_AUDIT; ++ } ++ ++ ret = 0; ++ ++exit_unlock: ++ mutex_unlock(&mp->lock); ++ return ret; ++} ++ ++/** ++ * kdbus_meta_proc_fake() - Fill process metadata from faked credentials ++ * @mp: Metadata ++ * @creds: Creds to set, may be %NULL ++ * @pids: PIDs to set, may be %NULL ++ * @seclabel: Seclabel to set, may be %NULL ++ * ++ * This function takes information stored in @creds, @pids and @seclabel and ++ * resolves them to kernel-representations, if possible. A call to this function ++ * is considered an alternative to calling kdbus_meta_add_current(), which ++ * derives the same information from the 'current' task. ++ * ++ * This call uses the current task's namespaces to resolve the given ++ * information. ++ * ++ * Return: 0 on success, negative error number otherwise. ++ */ ++int kdbus_meta_proc_fake(struct kdbus_meta_proc *mp, ++ const struct kdbus_creds *creds, ++ const struct kdbus_pids *pids, ++ const char *seclabel) ++{ ++ int ret; ++ ++ if (!mp) ++ return 0; ++ ++ mutex_lock(&mp->lock); ++ ++ if (creds && !(mp->collected & KDBUS_ATTACH_CREDS)) { ++ struct user_namespace *ns = current_user_ns(); ++ ++ mp->uid = make_kuid(ns, creds->uid); ++ mp->euid = make_kuid(ns, creds->euid); ++ mp->suid = make_kuid(ns, creds->suid); ++ mp->fsuid = make_kuid(ns, creds->fsuid); ++ ++ mp->gid = make_kgid(ns, creds->gid); ++ mp->egid = make_kgid(ns, creds->egid); ++ mp->sgid = make_kgid(ns, creds->sgid); ++ mp->fsgid = make_kgid(ns, creds->fsgid); ++ ++ if ((creds->uid != (uid_t)-1 && !uid_valid(mp->uid)) || ++ (creds->euid != (uid_t)-1 && !uid_valid(mp->euid)) || ++ (creds->suid != (uid_t)-1 && !uid_valid(mp->suid)) || ++ (creds->fsuid != (uid_t)-1 && !uid_valid(mp->fsuid)) || ++ (creds->gid != (gid_t)-1 && !gid_valid(mp->gid)) || ++ (creds->egid != (gid_t)-1 && !gid_valid(mp->egid)) || ++ (creds->sgid != (gid_t)-1 && !gid_valid(mp->sgid)) || ++ (creds->fsgid != (gid_t)-1 && !gid_valid(mp->fsgid))) { ++ ret = -EINVAL; ++ goto exit_unlock; ++ } ++ ++ mp->valid |= KDBUS_ATTACH_CREDS; ++ mp->collected |= KDBUS_ATTACH_CREDS; ++ } ++ ++ if (pids && !(mp->collected & KDBUS_ATTACH_PIDS)) { ++ mp->pid = get_pid(find_vpid(pids->tid)); ++ mp->tgid = get_pid(find_vpid(pids->pid)); ++ mp->ppid = get_pid(find_vpid(pids->ppid)); ++ ++ if ((pids->tid != 0 && !mp->pid) || ++ (pids->pid != 0 && !mp->tgid) || ++ (pids->ppid != 0 && !mp->ppid)) { ++ put_pid(mp->pid); ++ put_pid(mp->tgid); ++ put_pid(mp->ppid); ++ mp->pid = NULL; ++ mp->tgid = NULL; ++ mp->ppid = NULL; ++ ret = -EINVAL; ++ goto exit_unlock; ++ } ++ ++ mp->valid |= KDBUS_ATTACH_PIDS; ++ mp->collected |= KDBUS_ATTACH_PIDS; ++ } ++ ++ if (seclabel && !(mp->collected & KDBUS_ATTACH_SECLABEL)) { ++ mp->seclabel = kstrdup(seclabel, GFP_KERNEL); ++ if (!mp->seclabel) { ++ ret = -ENOMEM; ++ goto exit_unlock; ++ } ++ ++ mp->valid |= KDBUS_ATTACH_SECLABEL; ++ mp->collected |= KDBUS_ATTACH_SECLABEL; ++ } ++ ++ ret = 0; ++ ++exit_unlock: ++ mutex_unlock(&mp->lock); ++ return ret; ++} ++ ++/** ++ * kdbus_meta_conn_new() - Create connection metadata object ++ * ++ * Return: Pointer to new object on success, ERR_PTR on failure. ++ */ ++struct kdbus_meta_conn *kdbus_meta_conn_new(void) ++{ ++ struct kdbus_meta_conn *mc; ++ ++ mc = kzalloc(sizeof(*mc), GFP_KERNEL); ++ if (!mc) ++ return ERR_PTR(-ENOMEM); ++ ++ kref_init(&mc->kref); ++ mutex_init(&mc->lock); ++ ++ return mc; ++} ++ ++static void kdbus_meta_conn_free(struct kref *kref) ++{ ++ struct kdbus_meta_conn *mc = ++ container_of(kref, struct kdbus_meta_conn, kref); ++ ++ kfree(mc->conn_description); ++ kfree(mc->owned_names_items); ++ kfree(mc); ++} ++ ++/** ++ * kdbus_meta_conn_ref() - Gain reference ++ * @mc: Connection metadata object ++ */ ++struct kdbus_meta_conn *kdbus_meta_conn_ref(struct kdbus_meta_conn *mc) ++{ ++ if (mc) ++ kref_get(&mc->kref); ++ return mc; ++} ++ ++/** ++ * kdbus_meta_conn_unref() - Drop reference ++ * @mc: Connection metadata object ++ */ ++struct kdbus_meta_conn *kdbus_meta_conn_unref(struct kdbus_meta_conn *mc) ++{ ++ if (mc) ++ kref_put(&mc->kref, kdbus_meta_conn_free); ++ return NULL; ++} ++ ++static void kdbus_meta_conn_collect_timestamp(struct kdbus_meta_conn *mc, ++ struct kdbus_kmsg *kmsg) ++{ ++ struct timespec ts; ++ ++ ktime_get_ts(&ts); ++ mc->ts.monotonic_ns = timespec_to_ns(&ts); ++ ++ ktime_get_real_ts(&ts); ++ mc->ts.realtime_ns = timespec_to_ns(&ts); ++ ++ if (kmsg) ++ mc->ts.seqnum = kmsg->seq; ++ ++ mc->valid |= KDBUS_ATTACH_TIMESTAMP; ++} ++ ++static int kdbus_meta_conn_collect_names(struct kdbus_meta_conn *mc, ++ struct kdbus_conn *conn) ++{ ++ const struct kdbus_name_entry *e; ++ struct kdbus_item *item; ++ size_t slen, size; ++ ++ lockdep_assert_held(&conn->ep->bus->name_registry->rwlock); ++ ++ size = 0; ++ list_for_each_entry(e, &conn->names_list, conn_entry) ++ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_name) + ++ strlen(e->name) + 1); ++ ++ if (!size) ++ return 0; ++ ++ item = kmalloc(size, GFP_KERNEL); ++ if (!item) ++ return -ENOMEM; ++ ++ mc->owned_names_items = item; ++ mc->owned_names_size = size; ++ ++ list_for_each_entry(e, &conn->names_list, conn_entry) { ++ slen = strlen(e->name) + 1; ++ kdbus_item_set(item, KDBUS_ITEM_OWNED_NAME, NULL, ++ sizeof(struct kdbus_name) + slen); ++ item->name.flags = e->flags; ++ memcpy(item->name.name, e->name, slen); ++ item = KDBUS_ITEM_NEXT(item); ++ } ++ ++ /* sanity check: the buffer should be completely written now */ ++ WARN_ON((u8 *)item != (u8 *)mc->owned_names_items + size); ++ ++ mc->valid |= KDBUS_ATTACH_NAMES; ++ return 0; ++} ++ ++static int kdbus_meta_conn_collect_description(struct kdbus_meta_conn *mc, ++ struct kdbus_conn *conn) ++{ ++ if (!conn->description) ++ return 0; ++ ++ mc->conn_description = kstrdup(conn->description, GFP_KERNEL); ++ if (!mc->conn_description) ++ return -ENOMEM; ++ ++ mc->valid |= KDBUS_ATTACH_CONN_DESCRIPTION; ++ return 0; ++} ++ ++/** ++ * kdbus_meta_conn_collect() - Collect connection metadata ++ * @mc: Message metadata object ++ * @kmsg: Kmsg to collect data from ++ * @conn: Connection to collect data from ++ * @what: Attach flags to collect ++ * ++ * This collects connection metadata from @kmsg and @conn and saves it in @mc. ++ * ++ * If KDBUS_ATTACH_NAMES is set in @what and @conn is non-NULL, the caller must ++ * hold the name-registry read-lock of conn->ep->bus->registry. ++ * ++ * Return: 0 on success, negative error code on failure. ++ */ ++int kdbus_meta_conn_collect(struct kdbus_meta_conn *mc, ++ struct kdbus_kmsg *kmsg, ++ struct kdbus_conn *conn, ++ u64 what) ++{ ++ int ret; ++ ++ if (!mc || !(what & (KDBUS_ATTACH_TIMESTAMP | ++ KDBUS_ATTACH_NAMES | ++ KDBUS_ATTACH_CONN_DESCRIPTION))) ++ return 0; ++ ++ mutex_lock(&mc->lock); ++ ++ if (kmsg && (what & KDBUS_ATTACH_TIMESTAMP) && ++ !(mc->collected & KDBUS_ATTACH_TIMESTAMP)) { ++ kdbus_meta_conn_collect_timestamp(mc, kmsg); ++ mc->collected |= KDBUS_ATTACH_TIMESTAMP; ++ } ++ ++ if (conn && (what & KDBUS_ATTACH_NAMES) && ++ !(mc->collected & KDBUS_ATTACH_NAMES)) { ++ ret = kdbus_meta_conn_collect_names(mc, conn); ++ if (ret < 0) ++ goto exit_unlock; ++ mc->collected |= KDBUS_ATTACH_NAMES; ++ } ++ ++ if (conn && (what & KDBUS_ATTACH_CONN_DESCRIPTION) && ++ !(mc->collected & KDBUS_ATTACH_CONN_DESCRIPTION)) { ++ ret = kdbus_meta_conn_collect_description(mc, conn); ++ if (ret < 0) ++ goto exit_unlock; ++ mc->collected |= KDBUS_ATTACH_CONN_DESCRIPTION; ++ } ++ ++ ret = 0; ++ ++exit_unlock: ++ mutex_unlock(&mc->lock); ++ return ret; ++} ++ ++/* ++ * kdbus_meta_export_prepare() - Prepare metadata for export ++ * @mp: Process metadata, or NULL ++ * @mc: Connection metadata, or NULL ++ * @mask: Pointer to mask of KDBUS_ATTACH_* flags to export ++ * @sz: Pointer to return the size needed by the metadata ++ * ++ * Does a conservative calculation of how much space metadata information ++ * will take up during export. It is 'conservative' because for string ++ * translations in namespaces, it will use the kernel namespaces, which is ++ * the longest possible version. ++ * ++ * The actual size consumed by kdbus_meta_export() may hence vary from the ++ * one reported here, but it is guaranteed never to be greater. ++ * ++ * Return: 0 on success, negative error number otherwise. ++ */ ++int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp, ++ struct kdbus_meta_conn *mc, ++ u64 *mask, size_t *sz) ++{ ++ char *exe_pathname = NULL; ++ void *exe_page = NULL; ++ size_t size = 0; ++ u64 valid = 0; ++ int ret = 0; ++ ++ if (mp) { ++ mutex_lock(&mp->lock); ++ valid |= mp->valid; ++ mutex_unlock(&mp->lock); ++ } ++ ++ if (mc) { ++ mutex_lock(&mc->lock); ++ valid |= mc->valid; ++ mutex_unlock(&mc->lock); ++ } ++ ++ *mask &= valid; ++ *mask &= kdbus_meta_attach_mask; ++ ++ if (!*mask) ++ goto exit; ++ ++ /* process metadata */ ++ ++ if (mp && (*mask & KDBUS_ATTACH_CREDS)) ++ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_creds)); ++ ++ if (mp && (*mask & KDBUS_ATTACH_PIDS)) ++ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_pids)); ++ ++ if (mp && (*mask & KDBUS_ATTACH_AUXGROUPS)) ++ size += KDBUS_ITEM_SIZE(mp->n_auxgrps * sizeof(u64)); ++ ++ if (mp && (*mask & KDBUS_ATTACH_TID_COMM)) ++ size += KDBUS_ITEM_SIZE(strlen(mp->tid_comm) + 1); ++ ++ if (mp && (*mask & KDBUS_ATTACH_PID_COMM)) ++ size += KDBUS_ITEM_SIZE(strlen(mp->pid_comm) + 1); ++ ++ if (mp && (*mask & KDBUS_ATTACH_EXE)) { ++ exe_page = (void *)__get_free_page(GFP_TEMPORARY); ++ if (!exe_page) { ++ ret = -ENOMEM; ++ goto exit; ++ } ++ ++ exe_pathname = d_path(&mp->exe_path, exe_page, PAGE_SIZE); ++ if (IS_ERR(exe_pathname)) { ++ ret = PTR_ERR(exe_pathname); ++ goto exit; ++ } ++ ++ size += KDBUS_ITEM_SIZE(strlen(exe_pathname) + 1); ++ free_page((unsigned long)exe_page); ++ } ++ ++ if (mp && (*mask & KDBUS_ATTACH_CMDLINE)) ++ size += KDBUS_ITEM_SIZE(strlen(mp->cmdline) + 1); ++ ++ if (mp && (*mask & KDBUS_ATTACH_CGROUP)) ++ size += KDBUS_ITEM_SIZE(strlen(mp->cgroup) + 1); ++ ++ if (mp && (*mask & KDBUS_ATTACH_CAPS)) ++ size += KDBUS_ITEM_SIZE(sizeof(mp->caps)); ++ ++ if (mp && (*mask & KDBUS_ATTACH_SECLABEL)) ++ size += KDBUS_ITEM_SIZE(strlen(mp->seclabel) + 1); ++ ++ if (mp && (*mask & KDBUS_ATTACH_AUDIT)) ++ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_audit)); ++ ++ /* connection metadata */ ++ ++ if (mc && (*mask & KDBUS_ATTACH_NAMES)) ++ size += mc->owned_names_size; ++ ++ if (mc && (*mask & KDBUS_ATTACH_CONN_DESCRIPTION)) ++ size += KDBUS_ITEM_SIZE(strlen(mc->conn_description) + 1); ++ ++ if (mc && (*mask & KDBUS_ATTACH_TIMESTAMP)) ++ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_timestamp)); ++ ++exit: ++ *sz = size; ++ ++ return ret; ++} ++ ++static int kdbus_meta_push_kvec(struct kvec *kvec, ++ struct kdbus_item_header *hdr, ++ u64 type, void *payload, ++ size_t payload_size, u64 *size) ++{ ++ hdr->type = type; ++ hdr->size = KDBUS_ITEM_HEADER_SIZE + payload_size; ++ kdbus_kvec_set(kvec++, hdr, sizeof(*hdr), size); ++ kdbus_kvec_set(kvec++, payload, payload_size, size); ++ return 2 + !!kdbus_kvec_pad(kvec++, size); ++} ++ ++/* This is equivalent to from_kuid_munged(), but maps INVALID_UID to itself */ ++static uid_t kdbus_from_kuid_keep(kuid_t uid) ++{ ++ return uid_valid(uid) ? ++ from_kuid_munged(current_user_ns(), uid) : ((uid_t)-1); ++} ++ ++/* This is equivalent to from_kgid_munged(), but maps INVALID_GID to itself */ ++static gid_t kdbus_from_kgid_keep(kgid_t gid) ++{ ++ return gid_valid(gid) ? ++ from_kgid_munged(current_user_ns(), gid) : ((gid_t)-1); ++} ++ ++/** ++ * kdbus_meta_export() - export information from metadata into a slice ++ * @mp: Process metadata, or NULL ++ * @mc: Connection metadata, or NULL ++ * @mask: Mask of KDBUS_ATTACH_* flags to export ++ * @slice: The slice to export to ++ * @offset: The offset inside @slice to write to ++ * @real_size: The real size the metadata consumed ++ * ++ * This function exports information from metadata into @slice at offset ++ * @offset inside that slice. Only information that is requested in @mask ++ * and that has been collected before is exported. ++ * ++ * In order to make sure not to write out of bounds, @mask must be the same ++ * value that was previously returned from kdbus_meta_export_prepare(). The ++ * function will, however, not necessarily write as many bytes as returned by ++ * kdbus_meta_export_prepare(); depending on the namespaces in question, it ++ * might use up less than that. ++ * ++ * All information will be translated using the current namespaces. ++ * ++ * Return: 0 on success, negative error number otherwise. ++ */ ++int kdbus_meta_export(struct kdbus_meta_proc *mp, ++ struct kdbus_meta_conn *mc, ++ u64 mask, ++ struct kdbus_pool_slice *slice, ++ off_t offset, ++ size_t *real_size) ++{ ++ struct user_namespace *user_ns = current_user_ns(); ++ struct kdbus_item_header item_hdr[13], *hdr; ++ char *exe_pathname = NULL; ++ struct kdbus_creds creds; ++ struct kdbus_pids pids; ++ void *exe_page = NULL; ++ struct kvec kvec[40]; ++ u64 *auxgrps = NULL; ++ size_t cnt = 0; ++ u64 size = 0; ++ int ret = 0; ++ ++ hdr = &item_hdr[0]; ++ ++ /* ++ * TODO: We currently have no sane way of translating a set of caps ++ * between different user namespaces. Until that changes, we have ++ * to drop such items. ++ */ ++ if (mp && mp->caps_namespace != user_ns) ++ mask &= ~KDBUS_ATTACH_CAPS; ++ ++ if (mask == 0) { ++ *real_size = 0; ++ return 0; ++ } ++ ++ /* process metadata */ ++ ++ if (mp && (mask & KDBUS_ATTACH_CREDS)) { ++ creds.uid = kdbus_from_kuid_keep(mp->uid); ++ creds.euid = kdbus_from_kuid_keep(mp->euid); ++ creds.suid = kdbus_from_kuid_keep(mp->suid); ++ creds.fsuid = kdbus_from_kuid_keep(mp->fsuid); ++ creds.gid = kdbus_from_kgid_keep(mp->gid); ++ creds.egid = kdbus_from_kgid_keep(mp->egid); ++ creds.sgid = kdbus_from_kgid_keep(mp->sgid); ++ creds.fsgid = kdbus_from_kgid_keep(mp->fsgid); ++ ++ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_CREDS, ++ &creds, sizeof(creds), &size); ++ } ++ ++ if (mp && (mask & KDBUS_ATTACH_PIDS)) { ++ pids.pid = pid_vnr(mp->tgid); ++ pids.tid = pid_vnr(mp->pid); ++ pids.ppid = pid_vnr(mp->ppid); ++ ++ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_PIDS, ++ &pids, sizeof(pids), &size); ++ } ++ ++ if (mp && (mask & KDBUS_ATTACH_AUXGROUPS)) { ++ size_t payload_size = mp->n_auxgrps * sizeof(u64); ++ int i; ++ ++ auxgrps = kmalloc(payload_size, GFP_KERNEL); ++ if (!auxgrps) { ++ ret = -ENOMEM; ++ goto exit; ++ } ++ ++ for (i = 0; i < mp->n_auxgrps; i++) ++ auxgrps[i] = from_kgid_munged(user_ns, mp->auxgrps[i]); ++ ++ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, ++ KDBUS_ITEM_AUXGROUPS, ++ auxgrps, payload_size, &size); ++ } ++ ++ if (mp && (mask & KDBUS_ATTACH_TID_COMM)) ++ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, ++ KDBUS_ITEM_TID_COMM, mp->tid_comm, ++ strlen(mp->tid_comm) + 1, &size); ++ ++ if (mp && (mask & KDBUS_ATTACH_PID_COMM)) ++ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, ++ KDBUS_ITEM_PID_COMM, mp->pid_comm, ++ strlen(mp->pid_comm) + 1, &size); ++ ++ if (mp && (mask & KDBUS_ATTACH_EXE)) { ++ struct path p; ++ ++ /* ++ * TODO: We need access to __d_path() so we can write the path ++ * relative to conn->root_path. Once upstream, we need ++ * EXPORT_SYMBOL(__d_path) or an equivalent of d_path() that ++ * takes the root path directly. Until then, we drop this item ++ * if the root-paths differ. ++ */ ++ ++ get_fs_root(current->fs, &p); ++ if (path_equal(&p, &mp->root_path)) { ++ exe_page = (void *)__get_free_page(GFP_TEMPORARY); ++ if (!exe_page) { ++ path_put(&p); ++ ret = -ENOMEM; ++ goto exit; ++ } ++ ++ exe_pathname = d_path(&mp->exe_path, exe_page, ++ PAGE_SIZE); ++ if (IS_ERR(exe_pathname)) { ++ path_put(&p); ++ ret = PTR_ERR(exe_pathname); ++ goto exit; ++ } ++ ++ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, ++ KDBUS_ITEM_EXE, ++ exe_pathname, ++ strlen(exe_pathname) + 1, ++ &size); ++ } ++ path_put(&p); ++ } ++ ++ if (mp && (mask & KDBUS_ATTACH_CMDLINE)) ++ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, ++ KDBUS_ITEM_CMDLINE, mp->cmdline, ++ strlen(mp->cmdline) + 1, &size); ++ ++ if (mp && (mask & KDBUS_ATTACH_CGROUP)) ++ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, ++ KDBUS_ITEM_CGROUP, mp->cgroup, ++ strlen(mp->cgroup) + 1, &size); ++ ++ if (mp && (mask & KDBUS_ATTACH_CAPS)) ++ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, ++ KDBUS_ITEM_CAPS, &mp->caps, ++ sizeof(mp->caps), &size); ++ ++ if (mp && (mask & KDBUS_ATTACH_SECLABEL)) ++ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, ++ KDBUS_ITEM_SECLABEL, mp->seclabel, ++ strlen(mp->seclabel) + 1, &size); ++ ++ if (mp && (mask & KDBUS_ATTACH_AUDIT)) { ++ struct kdbus_audit a = { ++ .loginuid = from_kuid(user_ns, mp->audit_loginuid), ++ .sessionid = mp->audit_sessionid, ++ }; ++ ++ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_AUDIT, ++ &a, sizeof(a), &size); ++ } ++ ++ /* connection metadata */ ++ ++ if (mc && (mask & KDBUS_ATTACH_NAMES)) ++ kdbus_kvec_set(&kvec[cnt++], mc->owned_names_items, ++ mc->owned_names_size, &size); ++ ++ if (mc && (mask & KDBUS_ATTACH_CONN_DESCRIPTION)) ++ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, ++ KDBUS_ITEM_CONN_DESCRIPTION, ++ mc->conn_description, ++ strlen(mc->conn_description) + 1, ++ &size); ++ ++ if (mc && (mask & KDBUS_ATTACH_TIMESTAMP)) ++ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, ++ KDBUS_ITEM_TIMESTAMP, &mc->ts, ++ sizeof(mc->ts), &size); ++ ++ ret = kdbus_pool_slice_copy_kvec(slice, offset, kvec, cnt, size); ++ *real_size = size; ++ ++exit: ++ kfree(auxgrps); ++ ++ if (exe_page) ++ free_page((unsigned long)exe_page); ++ ++ return ret; ++} ++ ++/** ++ * kdbus_meta_calc_attach_flags() - calculate attach flags for a sender ++ * and a receiver ++ * @sender: Sending connection ++ * @receiver: Receiving connection ++ * ++ * Return: the attach flags both the sender and the receiver have opted-in ++ * for. ++ */ ++u64 kdbus_meta_calc_attach_flags(const struct kdbus_conn *sender, ++ const struct kdbus_conn *receiver) ++{ ++ return atomic64_read(&sender->attach_flags_send) & ++ atomic64_read(&receiver->attach_flags_recv); ++} +diff --git a/ipc/kdbus/metadata.h b/ipc/kdbus/metadata.h +new file mode 100644 +index 000000000000..42c942b34d2c +--- /dev/null ++++ b/ipc/kdbus/metadata.h +@@ -0,0 +1,57 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#ifndef __KDBUS_METADATA_H ++#define __KDBUS_METADATA_H ++ ++#include <linux/kernel.h> ++ ++struct kdbus_conn; ++struct kdbus_kmsg; ++struct kdbus_pool_slice; ++ ++struct kdbus_meta_proc; ++struct kdbus_meta_conn; ++ ++extern unsigned long long kdbus_meta_attach_mask; ++ ++struct kdbus_meta_proc *kdbus_meta_proc_new(void); ++struct kdbus_meta_proc *kdbus_meta_proc_ref(struct kdbus_meta_proc *mp); ++struct kdbus_meta_proc *kdbus_meta_proc_unref(struct kdbus_meta_proc *mp); ++int kdbus_meta_proc_collect(struct kdbus_meta_proc *mp, u64 what); ++int kdbus_meta_proc_fake(struct kdbus_meta_proc *mp, ++ const struct kdbus_creds *creds, ++ const struct kdbus_pids *pids, ++ const char *seclabel); ++ ++struct kdbus_meta_conn *kdbus_meta_conn_new(void); ++struct kdbus_meta_conn *kdbus_meta_conn_ref(struct kdbus_meta_conn *mc); ++struct kdbus_meta_conn *kdbus_meta_conn_unref(struct kdbus_meta_conn *mc); ++int kdbus_meta_conn_collect(struct kdbus_meta_conn *mc, ++ struct kdbus_kmsg *kmsg, ++ struct kdbus_conn *conn, ++ u64 what); ++ ++int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp, ++ struct kdbus_meta_conn *mc, ++ u64 *mask, size_t *sz); ++int kdbus_meta_export(struct kdbus_meta_proc *mp, ++ struct kdbus_meta_conn *mc, ++ u64 mask, ++ struct kdbus_pool_slice *slice, ++ off_t offset, size_t *real_size); ++u64 kdbus_meta_calc_attach_flags(const struct kdbus_conn *sender, ++ const struct kdbus_conn *receiver); ++ ++#endif diff --git a/kdbus-add-connection-pool-implementation.patch b/kdbus-add-connection-pool-implementation.patch new file mode 100644 index 000000000..ed8d7b9a6 --- /dev/null +++ b/kdbus-add-connection-pool-implementation.patch @@ -0,0 +1,822 @@ +From: Daniel Mack <daniel@zonque.org> +Date: Thu, 11 Sep 2014 18:56:41 +0200 +Subject: [PATCH] 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: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Djalal Harouni <tixxdz@opendz.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + ipc/kdbus/pool.c | 728 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ + ipc/kdbus/pool.h | 46 ++++ + 2 files changed, 774 insertions(+) + create mode 100644 ipc/kdbus/pool.c + create mode 100644 ipc/kdbus/pool.h + +diff --git a/ipc/kdbus/pool.c b/ipc/kdbus/pool.c +new file mode 100644 +index 000000000000..139bb77056b3 +--- /dev/null ++++ b/ipc/kdbus/pool.c +@@ -0,0 +1,728 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#include <linux/aio.h> ++#include <linux/file.h> ++#include <linux/fs.h> ++#include <linux/highmem.h> ++#include <linux/init.h> ++#include <linux/mm.h> ++#include <linux/module.h> ++#include <linux/pagemap.h> ++#include <linux/rbtree.h> ++#include <linux/sched.h> ++#include <linux/shmem_fs.h> ++#include <linux/sizes.h> ++#include <linux/slab.h> ++#include <linux/uaccess.h> ++#include <linux/uio.h> ++ ++#include "pool.h" ++#include "util.h" ++ ++/** ++ * struct kdbus_pool - the receiver's buffer ++ * @f: The backing shmem file ++ * @size: The size of the file ++ * @accounted_size: Currently accounted memory in bytes ++ * @lock: Pool data lock ++ * @slices: All slices sorted by address ++ * @slices_busy: Tree of allocated slices ++ * @slices_free: Tree of free slices ++ * ++ * The receiver's buffer, managed as a pool of allocated and free ++ * slices containing the queued messages. ++ * ++ * Messages sent with KDBUS_CMD_SEND are copied direcly by the ++ * sending process into the receiver's pool. ++ * ++ * Messages received with KDBUS_CMD_RECV just return the offset ++ * to the data placed in the pool. ++ * ++ * The internally allocated memory needs to be returned by the receiver ++ * with KDBUS_CMD_FREE. ++ */ ++struct kdbus_pool { ++ struct file *f; ++ size_t size; ++ size_t accounted_size; ++ struct mutex lock; ++ ++ struct list_head slices; ++ struct rb_root slices_busy; ++ struct rb_root slices_free; ++}; ++ ++/** ++ * struct kdbus_pool_slice - allocated element in kdbus_pool ++ * @pool: Pool this slice belongs to ++ * @off: Offset of slice in the shmem file ++ * @size: Size of slice ++ * @entry: Entry in "all slices" list ++ * @rb_node: Entry in free or busy list ++ * @free: Unused slice ++ * @accounted: Accounted as queue slice ++ * @ref_kernel: Kernel holds a reference ++ * @ref_user: Userspace holds a reference ++ * ++ * The pool has one or more slices, always spanning the entire size of the ++ * pool. ++ * ++ * Every slice is an element in a list sorted by the buffer address, to ++ * provide access to the next neighbor slice. ++ * ++ * Every slice is member in either the busy or the free tree. The free ++ * tree is organized by slice size, the busy tree organized by buffer ++ * offset. ++ */ ++struct kdbus_pool_slice { ++ struct kdbus_pool *pool; ++ size_t off; ++ size_t size; ++ ++ struct list_head entry; ++ struct rb_node rb_node; ++ ++ bool free:1; ++ bool accounted:1; ++ bool ref_kernel:1; ++ bool ref_user:1; ++}; ++ ++static struct kdbus_pool_slice *kdbus_pool_slice_new(struct kdbus_pool *pool, ++ size_t off, size_t size) ++{ ++ struct kdbus_pool_slice *slice; ++ ++ slice = kzalloc(sizeof(*slice), GFP_KERNEL); ++ if (!slice) ++ return NULL; ++ ++ slice->pool = pool; ++ slice->off = off; ++ slice->size = size; ++ slice->free = true; ++ return slice; ++} ++ ++/* insert a slice into the free tree */ ++static void kdbus_pool_add_free_slice(struct kdbus_pool *pool, ++ struct kdbus_pool_slice *slice) ++{ ++ struct rb_node **n; ++ struct rb_node *pn = NULL; ++ ++ n = &pool->slices_free.rb_node; ++ while (*n) { ++ struct kdbus_pool_slice *pslice; ++ ++ pn = *n; ++ pslice = rb_entry(pn, struct kdbus_pool_slice, rb_node); ++ if (slice->size < pslice->size) ++ n = &pn->rb_left; ++ else ++ n = &pn->rb_right; ++ } ++ ++ rb_link_node(&slice->rb_node, pn, n); ++ rb_insert_color(&slice->rb_node, &pool->slices_free); ++} ++ ++/* insert a slice into the busy tree */ ++static void kdbus_pool_add_busy_slice(struct kdbus_pool *pool, ++ struct kdbus_pool_slice *slice) ++{ ++ struct rb_node **n; ++ struct rb_node *pn = NULL; ++ ++ n = &pool->slices_busy.rb_node; ++ while (*n) { ++ struct kdbus_pool_slice *pslice; ++ ++ pn = *n; ++ pslice = rb_entry(pn, struct kdbus_pool_slice, rb_node); ++ if (slice->off < pslice->off) ++ n = &pn->rb_left; ++ else if (slice->off > pslice->off) ++ n = &pn->rb_right; ++ else ++ BUG(); ++ } ++ ++ rb_link_node(&slice->rb_node, pn, n); ++ rb_insert_color(&slice->rb_node, &pool->slices_busy); ++} ++ ++static struct kdbus_pool_slice *kdbus_pool_find_slice(struct kdbus_pool *pool, ++ size_t off) ++{ ++ struct rb_node *n; ++ ++ n = pool->slices_busy.rb_node; ++ while (n) { ++ struct kdbus_pool_slice *s; ++ ++ s = rb_entry(n, struct kdbus_pool_slice, rb_node); ++ if (off < s->off) ++ n = n->rb_left; ++ else if (off > s->off) ++ n = n->rb_right; ++ else ++ return s; ++ } ++ ++ return NULL; ++} ++ ++/** ++ * kdbus_pool_slice_alloc() - allocate memory from a pool ++ * @pool: The receiver's pool ++ * @size: The number of bytes to allocate ++ * @accounted: Whether this slice should be accounted for ++ * ++ * The returned slice is used for kdbus_pool_slice_release() to ++ * free the allocated memory. If either @kvec or @iovec is non-NULL, the data ++ * will be copied from kernel or userspace memory into the new slice at ++ * offset 0. ++ * ++ * Return: the allocated slice on success, ERR_PTR on failure. ++ */ ++struct kdbus_pool_slice *kdbus_pool_slice_alloc(struct kdbus_pool *pool, ++ size_t size, bool accounted) ++{ ++ size_t slice_size = KDBUS_ALIGN8(size); ++ struct rb_node *n, *found = NULL; ++ struct kdbus_pool_slice *s; ++ int ret = 0; ++ ++ if (WARN_ON(!size)) ++ return ERR_PTR(-EINVAL); ++ ++ /* search a free slice with the closest matching size */ ++ mutex_lock(&pool->lock); ++ n = pool->slices_free.rb_node; ++ while (n) { ++ s = rb_entry(n, struct kdbus_pool_slice, rb_node); ++ if (slice_size < s->size) { ++ found = n; ++ n = n->rb_left; ++ } else if (slice_size > s->size) { ++ n = n->rb_right; ++ } else { ++ found = n; ++ break; ++ } ++ } ++ ++ /* no slice with the minimum size found in the pool */ ++ if (!found) { ++ ret = -EXFULL; ++ goto exit_unlock; ++ } ++ ++ /* no exact match, use the closest one */ ++ if (!n) { ++ struct kdbus_pool_slice *s_new; ++ ++ s = rb_entry(found, struct kdbus_pool_slice, rb_node); ++ ++ /* split-off the remainder of the size to its own slice */ ++ s_new = kdbus_pool_slice_new(pool, s->off + slice_size, ++ s->size - slice_size); ++ if (!s_new) { ++ ret = -ENOMEM; ++ goto exit_unlock; ++ } ++ ++ list_add(&s_new->entry, &s->entry); ++ kdbus_pool_add_free_slice(pool, s_new); ++ ++ /* adjust our size now that we split-off another slice */ ++ s->size = slice_size; ++ } ++ ++ /* move slice from free to the busy tree */ ++ rb_erase(found, &pool->slices_free); ++ kdbus_pool_add_busy_slice(pool, s); ++ ++ WARN_ON(s->ref_kernel || s->ref_user); ++ ++ s->ref_kernel = true; ++ s->free = false; ++ s->accounted = accounted; ++ if (accounted) ++ pool->accounted_size += s->size; ++ mutex_unlock(&pool->lock); ++ ++ return s; ++ ++exit_unlock: ++ mutex_unlock(&pool->lock); ++ return ERR_PTR(ret); ++} ++ ++static void __kdbus_pool_slice_release(struct kdbus_pool_slice *slice) ++{ ++ struct kdbus_pool *pool = slice->pool; ++ ++ /* don't free the slice if either has a reference */ ++ if (slice->ref_kernel || slice->ref_user) ++ return; ++ ++ if (WARN_ON(slice->free)) ++ return; ++ ++ rb_erase(&slice->rb_node, &pool->slices_busy); ++ ++ /* merge with the next free slice */ ++ if (!list_is_last(&slice->entry, &pool->slices)) { ++ struct kdbus_pool_slice *s; ++ ++ s = list_entry(slice->entry.next, ++ struct kdbus_pool_slice, entry); ++ if (s->free) { ++ rb_erase(&s->rb_node, &pool->slices_free); ++ list_del(&s->entry); ++ slice->size += s->size; ++ kfree(s); ++ } ++ } ++ ++ /* merge with previous free slice */ ++ if (pool->slices.next != &slice->entry) { ++ struct kdbus_pool_slice *s; ++ ++ s = list_entry(slice->entry.prev, ++ struct kdbus_pool_slice, entry); ++ if (s->free) { ++ rb_erase(&s->rb_node, &pool->slices_free); ++ list_del(&slice->entry); ++ s->size += slice->size; ++ kfree(slice); ++ slice = s; ++ } ++ } ++ ++ slice->free = true; ++ kdbus_pool_add_free_slice(pool, slice); ++} ++ ++/** ++ * kdbus_pool_slice_release() - drop kernel-reference on allocated slice ++ * @slice: Slice allocated from the pool ++ * ++ * This releases the kernel-reference on the given slice. If the ++ * kernel-reference and the user-reference on a slice are dropped, the slice is ++ * returned to the pool. ++ * ++ * So far, we do not implement full ref-counting on slices. Each, kernel and ++ * user-space can have exactly one reference to a slice. If both are dropped at ++ * the same time, the slice is released. ++ */ ++void kdbus_pool_slice_release(struct kdbus_pool_slice *slice) ++{ ++ struct kdbus_pool *pool; ++ ++ if (!slice) ++ return; ++ ++ /* @slice may be freed, so keep local ptr to @pool */ ++ pool = slice->pool; ++ ++ mutex_lock(&pool->lock); ++ /* kernel must own a ref to @slice to drop it */ ++ WARN_ON(!slice->ref_kernel); ++ slice->ref_kernel = false; ++ /* no longer kernel-owned, de-account slice */ ++ if (slice->accounted && !WARN_ON(pool->accounted_size < slice->size)) ++ pool->accounted_size -= slice->size; ++ __kdbus_pool_slice_release(slice); ++ mutex_unlock(&pool->lock); ++} ++ ++/** ++ * kdbus_pool_release_offset() - release a public offset ++ * @pool: pool to operate on ++ * @off: offset to release ++ * ++ * This should be called whenever user-space frees a slice given to them. It ++ * verifies the slice is available and public, and then drops it. It ensures ++ * correct locking and barriers against queues. ++ * ++ * Return: 0 on success, ENXIO if the offset is invalid or not public. ++ */ ++int kdbus_pool_release_offset(struct kdbus_pool *pool, size_t off) ++{ ++ struct kdbus_pool_slice *slice; ++ int ret = 0; ++ ++ /* 'pool->size' is used as dummy offset for empty slices */ ++ if (off == pool->size) ++ return 0; ++ ++ mutex_lock(&pool->lock); ++ slice = kdbus_pool_find_slice(pool, off); ++ if (slice && slice->ref_user) { ++ slice->ref_user = false; ++ __kdbus_pool_slice_release(slice); ++ } else { ++ ret = -ENXIO; ++ } ++ mutex_unlock(&pool->lock); ++ ++ return ret; ++} ++ ++/** ++ * kdbus_pool_publish_empty() - publish empty slice to user-space ++ * @pool: pool to operate on ++ * @off: output storage for offset, or NULL ++ * @size: output storage for size, or NULL ++ * ++ * This is the same as kdbus_pool_slice_publish(), but uses a dummy slice with ++ * size 0. The returned offset points to the end of the pool and is never ++ * returned on real slices. ++ */ ++void kdbus_pool_publish_empty(struct kdbus_pool *pool, u64 *off, u64 *size) ++{ ++ if (off) ++ *off = pool->size; ++ if (size) ++ *size = 0; ++} ++ ++/** ++ * kdbus_pool_slice_publish() - publish slice to user-space ++ * @slice: The slice ++ * @out_offset: Output storage for offset, or NULL ++ * @out_size: Output storage for size, or NULL ++ * ++ * This prepares a slice to be published to user-space. ++ * ++ * This call combines the following operations: ++ * * the memory region is flushed so the user's memory view is consistent ++ * * the slice is marked as referenced by user-space, so user-space has to ++ * call KDBUS_CMD_FREE to release it ++ * * the offset and size of the slice are written to the given output ++ * arguments, if non-NULL ++ */ ++void kdbus_pool_slice_publish(struct kdbus_pool_slice *slice, ++ u64 *out_offset, u64 *out_size) ++{ ++ mutex_lock(&slice->pool->lock); ++ /* kernel must own a ref to @slice to gain a user-space ref */ ++ WARN_ON(!slice->ref_kernel); ++ slice->ref_user = true; ++ mutex_unlock(&slice->pool->lock); ++ ++ if (out_offset) ++ *out_offset = slice->off; ++ if (out_size) ++ *out_size = slice->size; ++} ++ ++/** ++ * kdbus_pool_slice_offset() - Get a slice's offset inside the pool ++ * @slice: Slice to return the offset of ++ * ++ * Return: The internal offset @slice inside the pool. ++ */ ++off_t kdbus_pool_slice_offset(const struct kdbus_pool_slice *slice) ++{ ++ return slice->off; ++} ++ ++/** ++ * kdbus_pool_slice_size() - get size of a pool slice ++ * @slice: slice to query ++ * ++ * Return: size of the given slice ++ */ ++size_t kdbus_pool_slice_size(const struct kdbus_pool_slice *slice) ++{ ++ return slice->size; ++} ++ ++/** ++ * kdbus_pool_new() - create a new pool ++ * @name: Name of the (deleted) file which shows up in ++ * /proc, used for debugging ++ * @size: Maximum size of the pool ++ * ++ * Return: a new kdbus_pool on success, ERR_PTR on failure. ++ */ ++struct kdbus_pool *kdbus_pool_new(const char *name, size_t size) ++{ ++ struct kdbus_pool_slice *s; ++ struct kdbus_pool *p; ++ struct file *f; ++ char *n = NULL; ++ int ret; ++ ++ p = kzalloc(sizeof(*p), GFP_KERNEL); ++ if (!p) ++ return ERR_PTR(-ENOMEM); ++ ++ if (name) { ++ n = kasprintf(GFP_KERNEL, KBUILD_MODNAME "-conn:%s", name); ++ if (!n) { ++ ret = -ENOMEM; ++ goto exit_free; ++ } ++ } ++ ++ f = shmem_file_setup(n ?: KBUILD_MODNAME "-conn", size, 0); ++ kfree(n); ++ ++ if (IS_ERR(f)) { ++ ret = PTR_ERR(f); ++ goto exit_free; ++ } ++ ++ ret = get_write_access(file_inode(f)); ++ if (ret < 0) ++ goto exit_put_shmem; ++ ++ /* allocate first slice spanning the entire pool */ ++ s = kdbus_pool_slice_new(p, 0, size); ++ if (!s) { ++ ret = -ENOMEM; ++ goto exit_put_write; ++ } ++ ++ p->f = f; ++ p->size = size; ++ p->slices_free = RB_ROOT; ++ p->slices_busy = RB_ROOT; ++ mutex_init(&p->lock); ++ ++ INIT_LIST_HEAD(&p->slices); ++ list_add(&s->entry, &p->slices); ++ ++ kdbus_pool_add_free_slice(p, s); ++ return p; ++ ++exit_put_write: ++ put_write_access(file_inode(f)); ++exit_put_shmem: ++ fput(f); ++exit_free: ++ kfree(p); ++ return ERR_PTR(ret); ++} ++ ++/** ++ * kdbus_pool_free() - destroy pool ++ * @pool: The receiver's pool ++ */ ++void kdbus_pool_free(struct kdbus_pool *pool) ++{ ++ struct kdbus_pool_slice *s, *tmp; ++ ++ if (!pool) ++ return; ++ ++ list_for_each_entry_safe(s, tmp, &pool->slices, entry) { ++ list_del(&s->entry); ++ kfree(s); ++ } ++ ++ put_write_access(file_inode(pool->f)); ++ fput(pool->f); ++ kfree(pool); ++} ++ ++/** ++ * kdbus_pool_accounted() - retrieve accounting information ++ * @pool: pool to query ++ * @size: output for overall pool size ++ * @acc: output for currently accounted size ++ * ++ * This returns accounting information of the pool. Note that the data might ++ * change after the function returns, as the pool lock is dropped. You need to ++ * protect the data via other means, if you need reliable accounting. ++ */ ++void kdbus_pool_accounted(struct kdbus_pool *pool, size_t *size, size_t *acc) ++{ ++ mutex_lock(&pool->lock); ++ if (size) ++ *size = pool->size; ++ if (acc) ++ *acc = pool->accounted_size; ++ mutex_unlock(&pool->lock); ++} ++ ++/** ++ * kdbus_pool_slice_copy_iovec() - copy user memory to a slice ++ * @slice: The slice to write to ++ * @off: Offset in the slice to write to ++ * @iov: iovec array, pointing to data to copy ++ * @iov_len: Number of elements in @iov ++ * @total_len: Total number of bytes described in members of @iov ++ * ++ * User memory referenced by @iov will be copied into @slice at offset @off. ++ * ++ * Return: the numbers of bytes copied, negative errno on failure. ++ */ ++ssize_t ++kdbus_pool_slice_copy_iovec(const struct kdbus_pool_slice *slice, loff_t off, ++ struct iovec *iov, size_t iov_len, size_t total_len) ++{ ++ struct iov_iter iter; ++ ssize_t len; ++ ++ if (WARN_ON(off + total_len > slice->size)) ++ return -EFAULT; ++ ++ off += slice->off; ++ iov_iter_init(&iter, WRITE, iov, iov_len, total_len); ++ len = vfs_iter_write(slice->pool->f, &iter, &off); ++ ++ return (len >= 0 && len != total_len) ? -EFAULT : len; ++} ++ ++/** ++ * kdbus_pool_slice_copy_kvec() - copy kernel memory to a slice ++ * @slice: The slice to write to ++ * @off: Offset in the slice to write to ++ * @kvec: kvec array, pointing to data to copy ++ * @kvec_len: Number of elements in @kvec ++ * @total_len: Total number of bytes described in members of @kvec ++ * ++ * Kernel memory referenced by @kvec will be copied into @slice at offset @off. ++ * ++ * Return: the numbers of bytes copied, negative errno on failure. ++ */ ++ssize_t kdbus_pool_slice_copy_kvec(const struct kdbus_pool_slice *slice, ++ loff_t off, struct kvec *kvec, ++ size_t kvec_len, size_t total_len) ++{ ++ struct iov_iter iter; ++ mm_segment_t old_fs; ++ ssize_t len; ++ ++ if (WARN_ON(off + total_len > slice->size)) ++ return -EFAULT; ++ ++ off += slice->off; ++ iov_iter_kvec(&iter, WRITE | ITER_KVEC, kvec, kvec_len, total_len); ++ ++ old_fs = get_fs(); ++ set_fs(get_ds()); ++ len = vfs_iter_write(slice->pool->f, &iter, &off); ++ set_fs(old_fs); ++ ++ return (len >= 0 && len != total_len) ? -EFAULT : len; ++} ++ ++/** ++ * kdbus_pool_slice_copy() - copy data from one slice into another ++ * @slice_dst: destination slice ++ * @slice_src: source slice ++ * ++ * Return: 0 on success, negative error number on failure. ++ */ ++int kdbus_pool_slice_copy(const struct kdbus_pool_slice *slice_dst, ++ const struct kdbus_pool_slice *slice_src) ++{ ++ struct file *f_src = slice_src->pool->f; ++ struct file *f_dst = slice_dst->pool->f; ++ struct inode *i_dst = file_inode(f_dst); ++ struct address_space *mapping_dst = f_dst->f_mapping; ++ const struct address_space_operations *aops = mapping_dst->a_ops; ++ unsigned long len = slice_src->size; ++ loff_t off_src = slice_src->off; ++ loff_t off_dst = slice_dst->off; ++ mm_segment_t old_fs; ++ int ret = 0; ++ ++ if (WARN_ON(slice_src->size != slice_dst->size) || ++ WARN_ON(slice_src->free || slice_dst->free)) ++ return -EINVAL; ++ ++ mutex_lock(&i_dst->i_mutex); ++ old_fs = get_fs(); ++ set_fs(get_ds()); ++ while (len > 0) { ++ unsigned long page_off; ++ unsigned long copy_len; ++ char __user *kaddr; ++ struct page *page; ++ ssize_t n_read; ++ void *fsdata; ++ long status; ++ ++ page_off = off_dst & (PAGE_CACHE_SIZE - 1); ++ copy_len = min_t(unsigned long, ++ PAGE_CACHE_SIZE - page_off, len); ++ ++ status = aops->write_begin(f_dst, mapping_dst, off_dst, ++ copy_len, 0, &page, &fsdata); ++ if (unlikely(status < 0)) { ++ ret = status; ++ break; ++ } ++ ++ kaddr = (char __force __user *)kmap(page) + page_off; ++ n_read = f_src->f_op->read(f_src, kaddr, copy_len, &off_src); ++ kunmap(page); ++ mark_page_accessed(page); ++ flush_dcache_page(page); ++ ++ if (unlikely(n_read != copy_len)) { ++ ret = -EFAULT; ++ break; ++ } ++ ++ status = aops->write_end(f_dst, mapping_dst, off_dst, ++ copy_len, copy_len, page, fsdata); ++ if (unlikely(status != copy_len)) { ++ ret = -EFAULT; ++ break; ++ } ++ ++ off_dst += copy_len; ++ len -= copy_len; ++ } ++ set_fs(old_fs); ++ mutex_unlock(&i_dst->i_mutex); ++ ++ return ret; ++} ++ ++/** ++ * kdbus_pool_mmap() - map the pool into the process ++ * @pool: The receiver's pool ++ * @vma: passed by mmap() syscall ++ * ++ * Return: the result of the mmap() call, negative errno on failure. ++ */ ++int kdbus_pool_mmap(const struct kdbus_pool *pool, struct vm_area_struct *vma) ++{ ++ /* deny write access to the pool */ ++ if (vma->vm_flags & VM_WRITE) ++ return -EPERM; ++ vma->vm_flags &= ~VM_MAYWRITE; ++ ++ /* do not allow to map more than the size of the file */ ++ if ((vma->vm_end - vma->vm_start) > pool->size) ++ return -EFAULT; ++ ++ /* replace the connection file with our shmem file */ ++ if (vma->vm_file) ++ fput(vma->vm_file); ++ vma->vm_file = get_file(pool->f); ++ ++ return pool->f->f_op->mmap(pool->f, vma); ++} +diff --git a/ipc/kdbus/pool.h b/ipc/kdbus/pool.h +new file mode 100644 +index 000000000000..a9038213aa4d +--- /dev/null ++++ b/ipc/kdbus/pool.h +@@ -0,0 +1,46 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> ++ * ++ * 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 ++ ++#include <linux/uio.h> ++ ++struct kdbus_pool; ++struct kdbus_pool_slice; ++ ++struct kdbus_pool *kdbus_pool_new(const char *name, size_t size); ++void kdbus_pool_free(struct kdbus_pool *pool); ++void kdbus_pool_accounted(struct kdbus_pool *pool, size_t *size, size_t *acc); ++int kdbus_pool_mmap(const struct kdbus_pool *pool, struct vm_area_struct *vma); ++int kdbus_pool_release_offset(struct kdbus_pool *pool, size_t off); ++void kdbus_pool_publish_empty(struct kdbus_pool *pool, u64 *off, u64 *size); ++ ++struct kdbus_pool_slice *kdbus_pool_slice_alloc(struct kdbus_pool *pool, ++ size_t size, bool accounted); ++void kdbus_pool_slice_release(struct kdbus_pool_slice *slice); ++void kdbus_pool_slice_publish(struct kdbus_pool_slice *slice, ++ u64 *out_offset, u64 *out_size); ++off_t kdbus_pool_slice_offset(const struct kdbus_pool_slice *slice); ++size_t kdbus_pool_slice_size(const struct kdbus_pool_slice *slice); ++int kdbus_pool_slice_copy(const struct kdbus_pool_slice *slice_dst, ++ const struct kdbus_pool_slice *slice_src); ++ssize_t kdbus_pool_slice_copy_kvec(const struct kdbus_pool_slice *slice, ++ loff_t off, struct kvec *kvec, ++ size_t kvec_count, size_t total_len); ++ssize_t kdbus_pool_slice_copy_iovec(const struct kdbus_pool_slice *slice, ++ loff_t off, struct iovec *iov, ++ size_t iov_count, size_t total_len); ++ ++#endif diff --git a/kdbus-add-connection-queue-handling-and-message-vali.patch b/kdbus-add-connection-queue-handling-and-message-vali.patch new file mode 100644 index 000000000..d1b8ddc24 --- /dev/null +++ b/kdbus-add-connection-queue-handling-and-message-vali.patch @@ -0,0 +1,4834 @@ +From: Daniel Mack <daniel@zonque.org> +Date: Thu, 11 Sep 2014 18:57:24 +0200 +Subject: [PATCH] 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: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Djalal Harouni <tixxdz@opendz.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + ipc/kdbus/connection.c | 2215 ++++++++++++++++++++++++++++++++++++++++++++++++ + ipc/kdbus/connection.h | 257 ++++++ + ipc/kdbus/item.c | 339 ++++++++ + ipc/kdbus/item.h | 64 ++ + ipc/kdbus/message.c | 616 ++++++++++++++ + ipc/kdbus/message.h | 133 +++ + ipc/kdbus/queue.c | 678 +++++++++++++++ + ipc/kdbus/queue.h | 92 ++ + ipc/kdbus/reply.c | 259 ++++++ + ipc/kdbus/reply.h | 68 ++ + ipc/kdbus/util.h | 2 +- + 11 files changed, 4722 insertions(+), 1 deletion(-) + create mode 100644 ipc/kdbus/connection.c + create mode 100644 ipc/kdbus/connection.h + create mode 100644 ipc/kdbus/item.c + create mode 100644 ipc/kdbus/item.h + create mode 100644 ipc/kdbus/message.c + create mode 100644 ipc/kdbus/message.h + create mode 100644 ipc/kdbus/queue.c + create mode 100644 ipc/kdbus/queue.h + create mode 100644 ipc/kdbus/reply.c + create mode 100644 ipc/kdbus/reply.h + +diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c +new file mode 100644 +index 000000000000..e554f1a71aa1 +--- /dev/null ++++ b/ipc/kdbus/connection.c +@@ -0,0 +1,2215 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#include <linux/audit.h> ++#include <linux/file.h> ++#include <linux/fs.h> ++#include <linux/fs_struct.h> ++#include <linux/hashtable.h> ++#include <linux/idr.h> ++#include <linux/init.h> ++#include <linux/math64.h> ++#include <linux/mm.h> ++#include <linux/module.h> ++#include <linux/mutex.h> ++#include <linux/path.h> ++#include <linux/poll.h> ++#include <linux/sched.h> ++#include <linux/shmem_fs.h> ++#include <linux/sizes.h> ++#include <linux/slab.h> ++#include <linux/syscalls.h> ++#include <linux/uio.h> ++ ++#include "bus.h" ++#include "connection.h" ++#include "endpoint.h" ++#include "handle.h" ++#include "match.h" ++#include "message.h" ++#include "metadata.h" ++#include "names.h" ++#include "domain.h" ++#include "item.h" ++#include "notify.h" ++#include "policy.h" ++#include "pool.h" ++#include "reply.h" ++#include "util.h" ++#include "queue.h" ++ ++#define KDBUS_CONN_ACTIVE_BIAS (INT_MIN + 2) ++#define KDBUS_CONN_ACTIVE_NEW (INT_MIN + 1) ++ ++static struct kdbus_conn *kdbus_conn_new(struct kdbus_ep *ep, bool privileged, ++ struct kdbus_cmd_hello *hello, ++ const char *name, ++ const struct kdbus_creds *creds, ++ const struct kdbus_pids *pids, ++ const char *seclabel, ++ const char *conn_description) ++{ ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++ static struct lock_class_key __key; ++#endif ++ struct kdbus_pool_slice *slice = NULL; ++ struct kdbus_bus *bus = ep->bus; ++ struct kdbus_conn *conn; ++ u64 attach_flags_send; ++ u64 attach_flags_recv; ++ u64 items_size = 0; ++ bool is_policy_holder; ++ bool is_activator; ++ bool is_monitor; ++ struct kvec kvec; ++ int ret; ++ ++ struct { ++ u64 size; ++ u64 type; ++ struct kdbus_bloom_parameter bloom; ++ } bloom_item; ++ ++ is_monitor = hello->flags & KDBUS_HELLO_MONITOR; ++ is_activator = hello->flags & KDBUS_HELLO_ACTIVATOR; ++ is_policy_holder = hello->flags & KDBUS_HELLO_POLICY_HOLDER; ++ ++ if (!hello->pool_size || !IS_ALIGNED(hello->pool_size, PAGE_SIZE)) ++ return ERR_PTR(-EINVAL); ++ if (is_monitor + is_activator + is_policy_holder > 1) ++ return ERR_PTR(-EINVAL); ++ if (name && !is_activator && !is_policy_holder) ++ return ERR_PTR(-EINVAL); ++ if (!name && (is_activator || is_policy_holder)) ++ return ERR_PTR(-EINVAL); ++ if (name && !kdbus_name_is_valid(name, true)) ++ return ERR_PTR(-EINVAL); ++ if (is_monitor && ep->user) ++ return ERR_PTR(-EOPNOTSUPP); ++ if (!privileged && (is_activator || is_policy_holder || is_monitor)) ++ return ERR_PTR(-EPERM); ++ if ((creds || pids || seclabel) && !privileged) ++ return ERR_PTR(-EPERM); ++ ++ ret = kdbus_sanitize_attach_flags(hello->attach_flags_send, ++ &attach_flags_send); ++ if (ret < 0) ++ return ERR_PTR(ret); ++ ++ ret = kdbus_sanitize_attach_flags(hello->attach_flags_recv, ++ &attach_flags_recv); ++ if (ret < 0) ++ return ERR_PTR(ret); ++ ++ /* The attach flags must always satisfy the bus requirements. */ ++ if (bus->attach_flags_req & ~attach_flags_send) ++ return ERR_PTR(-ECONNREFUSED); ++ ++ conn = kzalloc(sizeof(*conn), GFP_KERNEL); ++ if (!conn) ++ return ERR_PTR(-ENOMEM); ++ ++ kref_init(&conn->kref); ++ atomic_set(&conn->active, KDBUS_CONN_ACTIVE_NEW); ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++ lockdep_init_map(&conn->dep_map, "s_active", &__key, 0); ++#endif ++ mutex_init(&conn->lock); ++ INIT_LIST_HEAD(&conn->names_list); ++ INIT_LIST_HEAD(&conn->names_queue_list); ++ INIT_LIST_HEAD(&conn->reply_list); ++ atomic_set(&conn->name_count, 0); ++ atomic_set(&conn->request_count, 0); ++ atomic_set(&conn->lost_count, 0); ++ INIT_DELAYED_WORK(&conn->work, kdbus_reply_list_scan_work); ++ conn->cred = get_current_cred(); ++ init_waitqueue_head(&conn->wait); ++ kdbus_queue_init(&conn->queue); ++ conn->privileged = privileged; ++ conn->ep = kdbus_ep_ref(ep); ++ conn->id = atomic64_inc_return(&bus->domain->last_id); ++ conn->flags = hello->flags; ++ atomic64_set(&conn->attach_flags_send, attach_flags_send); ++ atomic64_set(&conn->attach_flags_recv, attach_flags_recv); ++ INIT_LIST_HEAD(&conn->monitor_entry); ++ ++ if (conn_description) { ++ conn->description = kstrdup(conn_description, GFP_KERNEL); ++ if (!conn->description) { ++ ret = -ENOMEM; ++ goto exit_unref; ++ } ++ } ++ ++ conn->pool = kdbus_pool_new(conn->description, hello->pool_size); ++ if (IS_ERR(conn->pool)) { ++ ret = PTR_ERR(conn->pool); ++ conn->pool = NULL; ++ goto exit_unref; ++ } ++ ++ conn->match_db = kdbus_match_db_new(); ++ if (IS_ERR(conn->match_db)) { ++ ret = PTR_ERR(conn->match_db); ++ conn->match_db = NULL; ++ goto exit_unref; ++ } ++ ++ /* return properties of this connection to the caller */ ++ hello->bus_flags = bus->bus_flags; ++ hello->id = conn->id; ++ ++ BUILD_BUG_ON(sizeof(bus->id128) != sizeof(hello->id128)); ++ memcpy(hello->id128, bus->id128, sizeof(hello->id128)); ++ ++ conn->meta = kdbus_meta_proc_new(); ++ if (IS_ERR(conn->meta)) { ++ ret = PTR_ERR(conn->meta); ++ conn->meta = NULL; ++ goto exit_unref; ++ } ++ ++ /* privileged processes can impersonate somebody else */ ++ if (creds || pids || seclabel) { ++ ret = kdbus_meta_proc_fake(conn->meta, creds, pids, seclabel); ++ if (ret < 0) ++ goto exit_unref; ++ ++ conn->faked_meta = true; ++ } else { ++ ret = kdbus_meta_proc_collect(conn->meta, ++ KDBUS_ATTACH_CREDS | ++ KDBUS_ATTACH_PIDS | ++ KDBUS_ATTACH_AUXGROUPS | ++ KDBUS_ATTACH_TID_COMM | ++ KDBUS_ATTACH_PID_COMM | ++ KDBUS_ATTACH_EXE | ++ KDBUS_ATTACH_CMDLINE | ++ KDBUS_ATTACH_CGROUP | ++ KDBUS_ATTACH_CAPS | ++ KDBUS_ATTACH_SECLABEL | ++ KDBUS_ATTACH_AUDIT); ++ if (ret < 0) ++ goto exit_unref; ++ } ++ ++ /* ++ * Account the connection against the current user (UID), or for ++ * custom endpoints use the anonymous user assigned to the endpoint. ++ * Note that limits are always accounted against the real UID, not ++ * the effective UID (cred->user always points to the accounting of ++ * cred->uid, not cred->euid). ++ */ ++ if (ep->user) { ++ conn->user = kdbus_user_ref(ep->user); ++ } else { ++ conn->user = kdbus_user_lookup(ep->bus->domain, current_uid()); ++ if (IS_ERR(conn->user)) { ++ ret = PTR_ERR(conn->user); ++ conn->user = NULL; ++ goto exit_unref; ++ } ++ } ++ ++ if (atomic_inc_return(&conn->user->connections) > KDBUS_USER_MAX_CONN) { ++ /* decremented by destructor as conn->user is valid */ ++ ret = -EMFILE; ++ goto exit_unref; ++ } ++ ++ bloom_item.size = sizeof(bloom_item); ++ bloom_item.type = KDBUS_ITEM_BLOOM_PARAMETER; ++ bloom_item.bloom = bus->bloom; ++ kdbus_kvec_set(&kvec, &bloom_item, bloom_item.size, &items_size); ++ ++ slice = kdbus_pool_slice_alloc(conn->pool, items_size, false); ++ if (IS_ERR(slice)) { ++ ret = PTR_ERR(slice); ++ slice = NULL; ++ goto exit_unref; ++ } ++ ++ ret = kdbus_pool_slice_copy_kvec(slice, 0, &kvec, 1, items_size); ++ if (ret < 0) ++ goto exit_unref; ++ ++ kdbus_pool_slice_publish(slice, &hello->offset, &hello->items_size); ++ kdbus_pool_slice_release(slice); ++ ++ return conn; ++ ++exit_unref: ++ kdbus_pool_slice_release(slice); ++ kdbus_conn_unref(conn); ++ return ERR_PTR(ret); ++} ++ ++static void __kdbus_conn_free(struct kref *kref) ++{ ++ struct kdbus_conn *conn = container_of(kref, struct kdbus_conn, kref); ++ ++ WARN_ON(kdbus_conn_active(conn)); ++ WARN_ON(delayed_work_pending(&conn->work)); ++ WARN_ON(!list_empty(&conn->queue.msg_list)); ++ WARN_ON(!list_empty(&conn->names_list)); ++ WARN_ON(!list_empty(&conn->names_queue_list)); ++ WARN_ON(!list_empty(&conn->reply_list)); ++ ++ if (conn->user) { ++ atomic_dec(&conn->user->connections); ++ kdbus_user_unref(conn->user); ++ } ++ ++ kdbus_meta_proc_unref(conn->meta); ++ kdbus_match_db_free(conn->match_db); ++ kdbus_pool_free(conn->pool); ++ kdbus_ep_unref(conn->ep); ++ put_cred(conn->cred); ++ kfree(conn->description); ++ kfree(conn->quota); ++ kfree(conn); ++} ++ ++/** ++ * kdbus_conn_ref() - take a connection reference ++ * @conn: Connection, may be %NULL ++ * ++ * Return: the connection itself ++ */ ++struct kdbus_conn *kdbus_conn_ref(struct kdbus_conn *conn) ++{ ++ if (conn) ++ kref_get(&conn->kref); ++ return conn; ++} ++ ++/** ++ * kdbus_conn_unref() - drop a connection reference ++ * @conn: Connection (may be NULL) ++ * ++ * When the last reference is dropped, the connection's internal structure ++ * is freed. ++ * ++ * Return: NULL ++ */ ++struct kdbus_conn *kdbus_conn_unref(struct kdbus_conn *conn) ++{ ++ if (conn) ++ kref_put(&conn->kref, __kdbus_conn_free); ++ return NULL; ++} ++ ++/** ++ * kdbus_conn_active() - connection is not disconnected ++ * @conn: Connection to check ++ * ++ * Return true if the connection was not disconnected, yet. Note that a ++ * connection might be disconnected asynchronously, unless you hold the ++ * connection lock. If that's not suitable for you, see kdbus_conn_acquire() to ++ * suppress connection shutdown for a short period. ++ * ++ * Return: true if the connection is still active ++ */ ++bool kdbus_conn_active(const struct kdbus_conn *conn) ++{ ++ return atomic_read(&conn->active) >= 0; ++} ++ ++/** ++ * kdbus_conn_acquire() - acquire an active connection reference ++ * @conn: Connection ++ * ++ * Users can close a connection via KDBUS_BYEBYE (or by destroying the ++ * endpoint/bus/...) at any time. Whenever this happens, we should deny any ++ * user-visible action on this connection and signal ECONNRESET instead. ++ * To avoid testing for connection availability everytime you take the ++ * connection-lock, you can acquire a connection for short periods. ++ * ++ * By calling kdbus_conn_acquire(), you gain an "active reference" to the ++ * connection. You must also hold a regular reference at any time! As long as ++ * you hold the active-ref, the connection will not be shut down. However, if ++ * the connection was shut down, you can never acquire an active-ref again. ++ * ++ * kdbus_conn_disconnect() disables the connection and then waits for all active ++ * references to be dropped. It will also wake up any pending operation. ++ * However, you must not sleep for an indefinite period while holding an ++ * active-reference. Otherwise, kdbus_conn_disconnect() might stall. If you need ++ * to sleep for an indefinite period, either release the reference and try to ++ * acquire it again after waking up, or make kdbus_conn_disconnect() wake up ++ * your wait-queue. ++ * ++ * Return: 0 on success, negative error code on failure. ++ */ ++int kdbus_conn_acquire(struct kdbus_conn *conn) ++{ ++ if (!atomic_inc_unless_negative(&conn->active)) ++ return -ECONNRESET; ++ ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++ rwsem_acquire_read(&conn->dep_map, 0, 1, _RET_IP_); ++#endif ++ ++ return 0; ++} ++ ++/** ++ * kdbus_conn_release() - release an active connection reference ++ * @conn: Connection ++ * ++ * This releases an active reference that has been acquired via ++ * kdbus_conn_acquire(). If the connection was already disabled and this is the ++ * last active-ref that is dropped, the disconnect-waiter will be woken up and ++ * properly close the connection. ++ */ ++void kdbus_conn_release(struct kdbus_conn *conn) ++{ ++ int v; ++ ++ if (!conn) ++ return; ++ ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++ rwsem_release(&conn->dep_map, 1, _RET_IP_); ++#endif ++ ++ v = atomic_dec_return(&conn->active); ++ if (v != KDBUS_CONN_ACTIVE_BIAS) ++ return; ++ ++ wake_up_all(&conn->wait); ++} ++ ++static int kdbus_conn_connect(struct kdbus_conn *conn, const char *name) ++{ ++ struct kdbus_ep *ep = conn->ep; ++ struct kdbus_bus *bus = ep->bus; ++ int ret; ++ ++ if (WARN_ON(atomic_read(&conn->active) != KDBUS_CONN_ACTIVE_NEW)) ++ return -EALREADY; ++ ++ /* make sure the ep-node is active while we add our connection */ ++ if (!kdbus_node_acquire(&ep->node)) ++ return -ESHUTDOWN; ++ ++ /* lock order: domain -> bus -> ep -> names -> conn */ ++ mutex_lock(&ep->lock); ++ down_write(&bus->conn_rwlock); ++ ++ /* link into monitor list */ ++ if (kdbus_conn_is_monitor(conn)) ++ list_add_tail(&conn->monitor_entry, &bus->monitors_list); ++ ++ /* link into bus and endpoint */ ++ list_add_tail(&conn->ep_entry, &ep->conn_list); ++ hash_add(bus->conn_hash, &conn->hentry, conn->id); ++ ++ /* enable lookups and acquire active ref */ ++ atomic_set(&conn->active, 1); ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++ rwsem_acquire_read(&conn->dep_map, 0, 1, _RET_IP_); ++#endif ++ ++ up_write(&bus->conn_rwlock); ++ mutex_unlock(&ep->lock); ++ ++ kdbus_node_release(&ep->node); ++ ++ /* ++ * Notify subscribers about the new active connection, unless it is ++ * a monitor. Monitors are invisible on the bus, can't be addressed ++ * directly, and won't cause any notifications. ++ */ ++ if (!kdbus_conn_is_monitor(conn)) { ++ ret = kdbus_notify_id_change(conn->ep->bus, KDBUS_ITEM_ID_ADD, ++ conn->id, conn->flags); ++ if (ret < 0) ++ goto exit_disconnect; ++ } ++ ++ if (kdbus_conn_is_activator(conn)) { ++ u64 flags = KDBUS_NAME_ACTIVATOR; ++ ++ if (WARN_ON(!name)) { ++ ret = -EINVAL; ++ goto exit_disconnect; ++ } ++ ++ ret = kdbus_name_acquire(bus->name_registry, conn, name, ++ flags, NULL); ++ if (ret < 0) ++ goto exit_disconnect; ++ } ++ ++ kdbus_conn_release(conn); ++ kdbus_notify_flush(bus); ++ return 0; ++ ++exit_disconnect: ++ kdbus_conn_release(conn); ++ kdbus_conn_disconnect(conn, false); ++ return ret; ++} ++ ++/** ++ * kdbus_conn_disconnect() - disconnect a connection ++ * @conn: The connection to disconnect ++ * @ensure_queue_empty: Flag to indicate if the call should fail in ++ * case the connection's message list is not ++ * empty ++ * ++ * If @ensure_msg_list_empty is true, and the connection has pending messages, ++ * -EBUSY is returned. ++ * ++ * Return: 0 on success, negative errno on failure ++ */ ++int kdbus_conn_disconnect(struct kdbus_conn *conn, bool ensure_queue_empty) ++{ ++ struct kdbus_queue_entry *entry, *tmp; ++ struct kdbus_bus *bus = conn->ep->bus; ++ struct kdbus_reply *r, *r_tmp; ++ struct kdbus_conn *c; ++ int i, v; ++ ++ mutex_lock(&conn->lock); ++ v = atomic_read(&conn->active); ++ if (v == KDBUS_CONN_ACTIVE_NEW) { ++ /* was never connected */ ++ mutex_unlock(&conn->lock); ++ return 0; ++ } ++ if (v < 0) { ++ /* already dead */ ++ mutex_unlock(&conn->lock); ++ return -ECONNRESET; ++ } ++ if (ensure_queue_empty && !list_empty(&conn->queue.msg_list)) { ++ /* still busy */ ++ mutex_unlock(&conn->lock); ++ return -EBUSY; ++ } ++ ++ atomic_add(KDBUS_CONN_ACTIVE_BIAS, &conn->active); ++ mutex_unlock(&conn->lock); ++ ++ wake_up_interruptible(&conn->wait); ++ ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++ rwsem_acquire(&conn->dep_map, 0, 0, _RET_IP_); ++ if (atomic_read(&conn->active) != KDBUS_CONN_ACTIVE_BIAS) ++ lock_contended(&conn->dep_map, _RET_IP_); ++#endif ++ ++ wait_event(conn->wait, ++ atomic_read(&conn->active) == KDBUS_CONN_ACTIVE_BIAS); ++ ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++ lock_acquired(&conn->dep_map, _RET_IP_); ++ rwsem_release(&conn->dep_map, 1, _RET_IP_); ++#endif ++ ++ cancel_delayed_work_sync(&conn->work); ++ kdbus_policy_remove_owner(&conn->ep->bus->policy_db, conn); ++ ++ /* lock order: domain -> bus -> ep -> names -> conn */ ++ mutex_lock(&conn->ep->lock); ++ down_write(&bus->conn_rwlock); ++ ++ /* remove from bus and endpoint */ ++ hash_del(&conn->hentry); ++ list_del(&conn->monitor_entry); ++ list_del(&conn->ep_entry); ++ ++ up_write(&bus->conn_rwlock); ++ mutex_unlock(&conn->ep->lock); ++ ++ /* ++ * Remove all names associated with this connection; this possibly ++ * moves queued messages back to the activator connection. ++ */ ++ kdbus_name_release_all(bus->name_registry, conn); ++ ++ /* if we die while other connections wait for our reply, notify them */ ++ mutex_lock(&conn->lock); ++ list_for_each_entry_safe(entry, tmp, &conn->queue.msg_list, entry) { ++ if (entry->reply) ++ kdbus_notify_reply_dead(bus, ++ entry->reply->reply_dst->id, ++ entry->reply->cookie); ++ kdbus_queue_entry_free(entry); ++ } ++ ++ list_for_each_entry_safe(r, r_tmp, &conn->reply_list, entry) ++ kdbus_reply_unlink(r); ++ mutex_unlock(&conn->lock); ++ ++ /* lock order: domain -> bus -> ep -> names -> conn */ ++ down_read(&bus->conn_rwlock); ++ hash_for_each(bus->conn_hash, i, c, hentry) { ++ mutex_lock(&c->lock); ++ list_for_each_entry_safe(r, r_tmp, &c->reply_list, entry) { ++ if (r->reply_src == conn) { ++ if (r->sync) { ++ kdbus_sync_reply_wakeup(r, -EPIPE); ++ kdbus_reply_unlink(r); ++ continue; ++ } ++ ++ /* send a 'connection dead' notification */ ++ kdbus_notify_reply_dead(bus, c->id, r->cookie); ++ kdbus_reply_unlink(r); ++ } ++ } ++ mutex_unlock(&c->lock); ++ } ++ up_read(&bus->conn_rwlock); ++ ++ if (!kdbus_conn_is_monitor(conn)) ++ kdbus_notify_id_change(bus, KDBUS_ITEM_ID_REMOVE, ++ conn->id, conn->flags); ++ ++ kdbus_notify_flush(bus); ++ ++ return 0; ++} ++ ++/** ++ * kdbus_conn_has_name() - check if a connection owns a name ++ * @conn: Connection ++ * @name: Well-know name to check for ++ * ++ * The caller must hold the registry lock of conn->ep->bus. ++ * ++ * Return: true if the name is currently owned by the connection ++ */ ++bool kdbus_conn_has_name(struct kdbus_conn *conn, const char *name) ++{ ++ struct kdbus_name_entry *e; ++ ++ lockdep_assert_held(&conn->ep->bus->name_registry->rwlock); ++ ++ list_for_each_entry(e, &conn->names_list, conn_entry) ++ if (strcmp(e->name, name) == 0) ++ return true; ++ ++ return false; ++} ++ ++struct kdbus_quota { ++ uint32_t memory; ++ uint16_t msgs; ++ uint8_t fds; ++}; ++ ++/** ++ * kdbus_conn_quota_inc() - increase quota accounting ++ * @c: connection owning the quota tracking ++ * @u: user to account for (or NULL for kernel accounting) ++ * @memory: size of memory to account for ++ * @fds: number of FDs to account for ++ * ++ * This call manages the quotas on resource @c. That is, it's used if other ++ * users want to use the resources of connection @c, which so far only concerns ++ * the receive queue of the destination. ++ * ++ * This increases the quota-accounting for user @u by @memory bytes and @fds ++ * file descriptors. If the user has already reached the quota limits, this call ++ * will not do any accounting but return a negative error code indicating the ++ * failure. ++ * ++ * Return: 0 on success, negative error code on failure. ++ */ ++int kdbus_conn_quota_inc(struct kdbus_conn *c, struct kdbus_user *u, ++ size_t memory, size_t fds) ++{ ++ struct kdbus_quota *quota; ++ size_t available, accounted; ++ unsigned int id; ++ ++ /* ++ * Pool Layout: ++ * 50% of a pool is always owned by the connection. It is reserved for ++ * kernel queries, handling received messages and other tasks that are ++ * under control of the pool owner. The other 50% of the pool are used ++ * as incoming queue. ++ * As we optionally support user-space based policies, we need fair ++ * allocation schemes. Furthermore, resource utilization should be ++ * maximized, so only minimal resources stay reserved. However, we need ++ * to adapt to a dynamic number of users, as we cannot know how many ++ * users will talk to a connection. Therefore, the current allocations ++ * works like this: ++ * We limit the number of bytes in a destination's pool per sending ++ * user. The space available for a user is 33% of the unused pool space ++ * (whereas the space used by the user itself is also treated as ++ * 'unused'). This way, we favor users coming first, but keep enough ++ * pool space available for any following users. Given that messages are ++ * dequeued in FIFO order, this should balance nicely if the number of ++ * users grows. At the same time, this algorithm guarantees that the ++ * space available to a connection is reduced dynamically, the more ++ * concurrent users talk to a connection. ++ */ ++ ++ /* per user-accounting is expensive, so we keep state small */ ++ BUILD_BUG_ON(sizeof(quota->memory) != 4); ++ BUILD_BUG_ON(sizeof(quota->msgs) != 2); ++ BUILD_BUG_ON(sizeof(quota->fds) != 1); ++ BUILD_BUG_ON(KDBUS_CONN_MAX_MSGS > U16_MAX); ++ BUILD_BUG_ON(KDBUS_CONN_MAX_FDS_PER_USER > U8_MAX); ++ ++ id = u ? u->id : KDBUS_USER_KERNEL_ID; ++ if (id >= c->n_quota) { ++ unsigned int users; ++ ++ users = max(KDBUS_ALIGN8(id) + 8, id); ++ quota = krealloc(c->quota, users * sizeof(*quota), ++ GFP_KERNEL | __GFP_ZERO); ++ if (!quota) ++ return -ENOMEM; ++ ++ c->n_quota = users; ++ c->quota = quota; ++ } ++ ++ quota = &c->quota[id]; ++ kdbus_pool_accounted(c->pool, &available, &accounted); ++ ++ /* half the pool is _always_ reserved for the pool owner */ ++ available /= 2; ++ ++ /* ++ * Pool owner slices are un-accounted slices; they can claim more ++ * than 50% of the queue. However, the slice we're dealing with here ++ * belong to the incoming queue, hence they are 'accounted' slices ++ * to which the 50%-limit applies. ++ */ ++ if (available < accounted) ++ return -ENOBUFS; ++ ++ /* 1/3 of the remaining space (including your own memory) */ ++ available = (available - accounted + quota->memory) / 3; ++ ++ if (available < quota->memory || ++ available - quota->memory < memory || ++ quota->memory + memory > U32_MAX) ++ return -ENOBUFS; ++ if (quota->msgs >= KDBUS_CONN_MAX_MSGS) ++ return -ENOBUFS; ++ if (quota->fds + fds < quota->fds || ++ quota->fds + fds > KDBUS_CONN_MAX_FDS_PER_USER) ++ return -EMFILE; ++ ++ quota->memory += memory; ++ quota->fds += fds; ++ ++quota->msgs; ++ return 0; ++} ++ ++/** ++ * kdbus_conn_quota_dec() - decrease quota accounting ++ * @c: connection owning the quota tracking ++ * @u: user which was accounted for (or NULL for kernel accounting) ++ * @memory: size of memory which was accounted for ++ * @fds: number of FDs which were accounted for ++ * ++ * This does the reverse of kdbus_conn_quota_inc(). You have to release any ++ * accounted resources that you called kdbus_conn_quota_inc() for. However, you ++ * must not call kdbus_conn_quota_dec() if the accounting failed (that is, ++ * kdbus_conn_quota_inc() failed). ++ */ ++void kdbus_conn_quota_dec(struct kdbus_conn *c, struct kdbus_user *u, ++ size_t memory, size_t fds) ++{ ++ struct kdbus_quota *quota; ++ unsigned int id; ++ ++ id = u ? u->id : KDBUS_USER_KERNEL_ID; ++ if (WARN_ON(id >= c->n_quota)) ++ return; ++ ++ quota = &c->quota[id]; ++ ++ if (!WARN_ON(quota->msgs == 0)) ++ --quota->msgs; ++ if (!WARN_ON(quota->memory < memory)) ++ quota->memory -= memory; ++ if (!WARN_ON(quota->fds < fds)) ++ quota->fds -= fds; ++} ++ ++/** ++ * kdbus_conn_lost_message() - handle lost messages ++ * @c: connection that lost a message ++ * ++ * kdbus is reliable. That means, we try hard to never lose messages. However, ++ * memory is limited, so we cannot rely on transmissions to never fail. ++ * Therefore, we use quota-limits to let callers know if there unicast message ++ * cannot be transmitted to a peer. This works fine for unicasts, but for ++ * broadcasts we cannot make the caller handle the transmission failure. ++ * Instead, we must let the destination know that it couldn't receive a ++ * broadcast. ++ * As this is an unlikely scenario, we keep it simple. A single lost-counter ++ * remembers the number of lost messages since the last call to RECV. The next ++ * message retrieval will notify the connection that it lost messages since the ++ * last message retrieval and thus should resync its state. ++ */ ++void kdbus_conn_lost_message(struct kdbus_conn *c) ++{ ++ if (atomic_inc_return(&c->lost_count) == 1) ++ wake_up_interruptible(&c->wait); ++} ++ ++/* Callers should take the conn_dst lock */ ++static struct kdbus_queue_entry * ++kdbus_conn_entry_make(struct kdbus_conn *conn_dst, ++ const struct kdbus_kmsg *kmsg, ++ struct kdbus_user *user) ++{ ++ struct kdbus_queue_entry *entry; ++ ++ /* The remote connection was disconnected */ ++ if (!kdbus_conn_active(conn_dst)) ++ return ERR_PTR(-ECONNRESET); ++ ++ /* ++ * If the connection does not accept file descriptors but the message ++ * has some attached, refuse it. ++ * ++ * If this is a monitor connection, accept the message. In that ++ * case, all file descriptors will be set to -1 at receive time. ++ */ ++ if (!kdbus_conn_is_monitor(conn_dst) && ++ !(conn_dst->flags & KDBUS_HELLO_ACCEPT_FD) && ++ kmsg->res && kmsg->res->fds_count > 0) ++ return ERR_PTR(-ECOMM); ++ ++ entry = kdbus_queue_entry_new(conn_dst, kmsg, user); ++ if (IS_ERR(entry)) ++ return entry; ++ ++ return entry; ++} ++ ++/* ++ * Synchronously responding to a message, allocate a queue entry ++ * and attach it to the reply tracking object. ++ * The connection's queue will never get to see it. ++ */ ++static int kdbus_conn_entry_sync_attach(struct kdbus_conn *conn_dst, ++ const struct kdbus_kmsg *kmsg, ++ struct kdbus_reply *reply_wake) ++{ ++ struct kdbus_queue_entry *entry; ++ int remote_ret; ++ int ret = 0; ++ ++ mutex_lock(&reply_wake->reply_dst->lock); ++ ++ /* ++ * If we are still waiting then proceed, allocate a queue ++ * entry and attach it to the reply object ++ */ ++ if (reply_wake->waiting) { ++ entry = kdbus_conn_entry_make(conn_dst, kmsg, ++ reply_wake->reply_src->user); ++ if (IS_ERR(entry)) ++ ret = PTR_ERR(entry); ++ else ++ /* Attach the entry to the reply object */ ++ reply_wake->queue_entry = entry; ++ } else { ++ ret = -ECONNRESET; ++ } ++ ++ /* ++ * Update the reply object and wake up remote peer only ++ * on appropriate return codes ++ * ++ * * -ECOMM: if the replying connection failed with -ECOMM ++ * then wakeup remote peer with -EREMOTEIO ++ * ++ * We do this to differenciate between -ECOMM errors ++ * from the original sender perspective: ++ * -ECOMM error during the sync send and ++ * -ECOMM error during the sync reply, this last ++ * one is rewritten to -EREMOTEIO ++ * ++ * * Wake up on all other return codes. ++ */ ++ remote_ret = ret; ++ ++ if (ret == -ECOMM) ++ remote_ret = -EREMOTEIO; ++ ++ kdbus_sync_reply_wakeup(reply_wake, remote_ret); ++ kdbus_reply_unlink(reply_wake); ++ mutex_unlock(&reply_wake->reply_dst->lock); ++ ++ return ret; ++} ++ ++/** ++ * kdbus_conn_entry_insert() - enqueue a message into the receiver's pool ++ * @conn_src: The sending connection ++ * @conn_dst: The connection to queue into ++ * @kmsg: The kmsg to queue ++ * @reply: The reply tracker to attach to the queue entry ++ * ++ * Return: 0 on success. negative error otherwise. ++ */ ++int kdbus_conn_entry_insert(struct kdbus_conn *conn_src, ++ struct kdbus_conn *conn_dst, ++ const struct kdbus_kmsg *kmsg, ++ struct kdbus_reply *reply) ++{ ++ struct kdbus_queue_entry *entry; ++ int ret; ++ ++ kdbus_conn_lock2(conn_src, conn_dst); ++ ++ entry = kdbus_conn_entry_make(conn_dst, kmsg, ++ conn_src ? conn_src->user : NULL); ++ if (IS_ERR(entry)) { ++ ret = PTR_ERR(entry); ++ goto exit_unlock; ++ } ++ ++ if (reply) { ++ kdbus_reply_link(reply); ++ if (!reply->sync) ++ schedule_delayed_work(&conn_src->work, 0); ++ } ++ ++ kdbus_queue_entry_enqueue(entry, reply); ++ wake_up_interruptible(&conn_dst->wait); ++ ++ ret = 0; ++ ++exit_unlock: ++ kdbus_conn_unlock2(conn_src, conn_dst); ++ return ret; ++} ++ ++static int kdbus_conn_wait_reply(struct kdbus_conn *conn_src, ++ struct kdbus_cmd_send *cmd_send, ++ struct file *ioctl_file, ++ struct file *cancel_fd, ++ struct kdbus_reply *reply_wait, ++ ktime_t expire) ++{ ++ struct kdbus_queue_entry *entry; ++ struct poll_wqueues pwq = {}; ++ int ret; ++ ++ if (WARN_ON(!reply_wait)) ++ return -EIO; ++ ++ /* ++ * Block until the reply arrives. reply_wait is left untouched ++ * by the timeout scans that might be conducted for other, ++ * asynchronous replies of conn_src. ++ */ ++ ++ poll_initwait(&pwq); ++ poll_wait(ioctl_file, &conn_src->wait, &pwq.pt); ++ ++ for (;;) { ++ /* ++ * Any of the following conditions will stop our synchronously ++ * blocking SEND command: ++ * ++ * a) The origin sender closed its connection ++ * b) The remote peer answered, setting reply_wait->waiting = 0 ++ * c) The cancel FD was written to ++ * d) A signal was received ++ * e) The specified timeout was reached, and none of the above ++ * conditions kicked in. ++ */ ++ ++ /* ++ * We have already acquired an active reference when ++ * entering here, but another thread may call ++ * KDBUS_CMD_BYEBYE which does not acquire an active ++ * reference, therefore kdbus_conn_disconnect() will ++ * not wait for us. ++ */ ++ if (!kdbus_conn_active(conn_src)) { ++ ret = -ECONNRESET; ++ break; ++ } ++ ++ /* ++ * After the replying peer unset the waiting variable ++ * it will wake up us. ++ */ ++ if (!reply_wait->waiting) { ++ ret = reply_wait->err; ++ break; ++ } ++ ++ if (cancel_fd) { ++ unsigned int r; ++ ++ r = cancel_fd->f_op->poll(cancel_fd, &pwq.pt); ++ if (r & POLLIN) { ++ ret = -ECANCELED; ++ break; ++ } ++ } ++ ++ if (signal_pending(current)) { ++ ret = -EINTR; ++ break; ++ } ++ ++ if (!poll_schedule_timeout(&pwq, TASK_INTERRUPTIBLE, ++ &expire, 0)) { ++ ret = -ETIMEDOUT; ++ break; ++ } ++ ++ /* ++ * Reset the poll worker func, so the waitqueues are not ++ * added to the poll table again. We just reuse what we've ++ * collected earlier for further iterations. ++ */ ++ init_poll_funcptr(&pwq.pt, NULL); ++ } ++ ++ poll_freewait(&pwq); ++ ++ if (ret == -EINTR) { ++ /* ++ * Interrupted system call. Unref the reply object, and pass ++ * the return value down the chain. Mark the reply as ++ * interrupted, so the cleanup work can remove it, but do not ++ * unlink it from the list. Once the syscall restarts, we'll ++ * pick it up and wait on it again. ++ */ ++ mutex_lock(&conn_src->lock); ++ reply_wait->interrupted = true; ++ schedule_delayed_work(&conn_src->work, 0); ++ mutex_unlock(&conn_src->lock); ++ ++ return -ERESTARTSYS; ++ } ++ ++ mutex_lock(&conn_src->lock); ++ reply_wait->waiting = false; ++ entry = reply_wait->queue_entry; ++ if (entry) { ++ ret = kdbus_queue_entry_install(entry, ++ &cmd_send->reply.return_flags, ++ true); ++ kdbus_pool_slice_publish(entry->slice, &cmd_send->reply.offset, ++ &cmd_send->reply.msg_size); ++ kdbus_queue_entry_free(entry); ++ } ++ kdbus_reply_unlink(reply_wait); ++ mutex_unlock(&conn_src->lock); ++ ++ return ret; ++} ++ ++static int kdbus_pin_dst(struct kdbus_bus *bus, ++ struct kdbus_kmsg *kmsg, ++ struct kdbus_name_entry **out_name, ++ struct kdbus_conn **out_dst) ++{ ++ struct kdbus_msg_resources *res = kmsg->res; ++ struct kdbus_name_entry *name = NULL; ++ struct kdbus_conn *dst = NULL; ++ struct kdbus_msg *msg = &kmsg->msg; ++ int ret; ++ ++ if (WARN_ON(!res)) ++ return -EINVAL; ++ ++ lockdep_assert_held(&bus->name_registry->rwlock); ++ ++ if (!res->dst_name) { ++ dst = kdbus_bus_find_conn_by_id(bus, msg->dst_id); ++ if (!dst) ++ return -ENXIO; ++ ++ if (!kdbus_conn_is_ordinary(dst)) { ++ ret = -ENXIO; ++ goto error; ++ } ++ } else { ++ name = kdbus_name_lookup_unlocked(bus->name_registry, ++ res->dst_name); ++ if (!name) ++ return -ESRCH; ++ ++ /* ++ * If both a name and a connection ID are given as destination ++ * of a message, check that the currently owning connection of ++ * the name matches the specified ID. ++ * This way, we allow userspace to send the message to a ++ * specific connection by ID only if the connection currently ++ * owns the given name. ++ */ ++ if (msg->dst_id != KDBUS_DST_ID_NAME && ++ msg->dst_id != name->conn->id) ++ return -EREMCHG; ++ ++ if (!name->conn && name->activator) ++ dst = kdbus_conn_ref(name->activator); ++ else ++ dst = kdbus_conn_ref(name->conn); ++ ++ if ((msg->flags & KDBUS_MSG_NO_AUTO_START) && ++ kdbus_conn_is_activator(dst)) { ++ ret = -EADDRNOTAVAIL; ++ goto error; ++ } ++ ++ /* ++ * Record the sequence number of the registered name; it will ++ * be passed on to the queue, in case messages addressed to a ++ * name need to be moved from or to an activator. ++ */ ++ kmsg->dst_name_id = name->name_id; ++ } ++ ++ *out_name = name; ++ *out_dst = dst; ++ return 0; ++ ++error: ++ kdbus_conn_unref(dst); ++ return ret; ++} ++ ++static int kdbus_conn_reply(struct kdbus_conn *src, struct kdbus_kmsg *kmsg) ++{ ++ struct kdbus_name_entry *name = NULL; ++ struct kdbus_reply *reply, *wake = NULL; ++ struct kdbus_conn *dst = NULL; ++ struct kdbus_bus *bus = src->ep->bus; ++ u64 attach; ++ int ret; ++ ++ if (WARN_ON(kmsg->msg.dst_id == KDBUS_DST_ID_BROADCAST) || ++ WARN_ON(kmsg->msg.flags & KDBUS_MSG_EXPECT_REPLY) || ++ WARN_ON(kmsg->msg.flags & KDBUS_MSG_SIGNAL)) ++ return -EINVAL; ++ ++ /* name-registry must be locked for lookup *and* collecting data */ ++ down_read(&bus->name_registry->rwlock); ++ ++ /* find and pin destination */ ++ ++ ret = kdbus_pin_dst(bus, kmsg, &name, &dst); ++ if (ret < 0) ++ goto exit; ++ ++ mutex_lock(&dst->lock); ++ reply = kdbus_reply_find(src, dst, kmsg->msg.cookie_reply); ++ if (reply) { ++ if (reply->sync) ++ wake = kdbus_reply_ref(reply); ++ kdbus_reply_unlink(reply); ++ } ++ mutex_unlock(&dst->lock); ++ ++ if (!reply) { ++ ret = -EPERM; ++ goto exit; ++ } ++ ++ /* attach metadata */ ++ ++ attach = kdbus_meta_calc_attach_flags(src, dst); ++ ++ if (!src->faked_meta) { ++ ret = kdbus_meta_proc_collect(kmsg->proc_meta, attach); ++ if (ret < 0) ++ goto exit; ++ } ++ ++ ret = kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, src, attach); ++ if (ret < 0) ++ goto exit; ++ ++ /* send message */ ++ ++ kdbus_bus_eavesdrop(bus, src, kmsg); ++ ++ if (wake) ++ ret = kdbus_conn_entry_sync_attach(dst, kmsg, wake); ++ else ++ ret = kdbus_conn_entry_insert(src, dst, kmsg, NULL); ++ ++exit: ++ up_read(&bus->name_registry->rwlock); ++ kdbus_reply_unref(wake); ++ kdbus_conn_unref(dst); ++ return ret; ++} ++ ++static struct kdbus_reply *kdbus_conn_call(struct kdbus_conn *src, ++ struct kdbus_kmsg *kmsg, ++ ktime_t exp) ++{ ++ struct kdbus_name_entry *name = NULL; ++ struct kdbus_reply *wait = NULL; ++ struct kdbus_conn *dst = NULL; ++ struct kdbus_bus *bus = src->ep->bus; ++ u64 attach; ++ int ret; ++ ++ if (WARN_ON(kmsg->msg.dst_id == KDBUS_DST_ID_BROADCAST) || ++ WARN_ON(kmsg->msg.flags & KDBUS_MSG_SIGNAL) || ++ WARN_ON(!(kmsg->msg.flags & KDBUS_MSG_EXPECT_REPLY))) ++ return ERR_PTR(-EINVAL); ++ ++ /* resume previous wait-context, if available */ ++ ++ mutex_lock(&src->lock); ++ wait = kdbus_reply_find(NULL, src, kmsg->msg.cookie); ++ if (wait) { ++ if (wait->interrupted) { ++ kdbus_reply_ref(wait); ++ wait->interrupted = false; ++ } else { ++ wait = NULL; ++ } ++ } ++ mutex_unlock(&src->lock); ++ ++ if (wait) ++ return wait; ++ ++ if (ktime_compare(ktime_get(), exp) >= 0) ++ return ERR_PTR(-ETIMEDOUT); ++ ++ /* name-registry must be locked for lookup *and* collecting data */ ++ down_read(&bus->name_registry->rwlock); ++ ++ /* find and pin destination */ ++ ++ ret = kdbus_pin_dst(bus, kmsg, &name, &dst); ++ if (ret < 0) ++ goto exit; ++ ++ if (!kdbus_conn_policy_talk(src, current_cred(), dst)) { ++ ret = -EPERM; ++ goto exit; ++ } ++ ++ wait = kdbus_reply_new(dst, src, &kmsg->msg, name, true); ++ if (IS_ERR(wait)) { ++ ret = PTR_ERR(wait); ++ wait = NULL; ++ goto exit; ++ } ++ ++ /* attach metadata */ ++ ++ attach = kdbus_meta_calc_attach_flags(src, dst); ++ ++ if (!src->faked_meta) { ++ ret = kdbus_meta_proc_collect(kmsg->proc_meta, attach); ++ if (ret < 0) ++ goto exit; ++ } ++ ++ ret = kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, src, attach); ++ if (ret < 0) ++ goto exit; ++ ++ /* send message */ ++ ++ kdbus_bus_eavesdrop(bus, src, kmsg); ++ ++ ret = kdbus_conn_entry_insert(src, dst, kmsg, wait); ++ if (ret < 0) ++ goto exit; ++ ++ ret = 0; ++ ++exit: ++ up_read(&bus->name_registry->rwlock); ++ if (ret < 0) { ++ kdbus_reply_unref(wait); ++ wait = ERR_PTR(ret); ++ } ++ kdbus_conn_unref(dst); ++ return wait; ++} ++ ++static int kdbus_conn_unicast(struct kdbus_conn *src, struct kdbus_kmsg *kmsg) ++{ ++ struct kdbus_name_entry *name = NULL; ++ struct kdbus_reply *wait = NULL; ++ struct kdbus_conn *dst = NULL; ++ struct kdbus_bus *bus = src->ep->bus; ++ bool is_signal = (kmsg->msg.flags & KDBUS_MSG_SIGNAL); ++ u64 attach; ++ int ret = 0; ++ ++ if (WARN_ON(kmsg->msg.dst_id == KDBUS_DST_ID_BROADCAST) || ++ WARN_ON(!(kmsg->msg.flags & KDBUS_MSG_EXPECT_REPLY) && ++ kmsg->msg.cookie_reply != 0)) ++ return -EINVAL; ++ ++ /* name-registry must be locked for lookup *and* collecting data */ ++ down_read(&bus->name_registry->rwlock); ++ ++ /* find and pin destination */ ++ ++ ret = kdbus_pin_dst(bus, kmsg, &name, &dst); ++ if (ret < 0) ++ goto exit; ++ ++ if (is_signal) { ++ /* like broadcasts we eavesdrop even if the msg is dropped */ ++ kdbus_bus_eavesdrop(bus, src, kmsg); ++ ++ /* drop silently if peer is not interested or not privileged */ ++ if (!kdbus_match_db_match_kmsg(dst->match_db, src, kmsg) || ++ !kdbus_conn_policy_talk(dst, NULL, src)) ++ goto exit; ++ } else if (!kdbus_conn_policy_talk(src, current_cred(), dst)) { ++ ret = -EPERM; ++ goto exit; ++ } else if (kmsg->msg.flags & KDBUS_MSG_EXPECT_REPLY) { ++ wait = kdbus_reply_new(dst, src, &kmsg->msg, name, false); ++ if (IS_ERR(wait)) { ++ ret = PTR_ERR(wait); ++ wait = NULL; ++ goto exit; ++ } ++ } ++ ++ /* attach metadata */ ++ ++ attach = kdbus_meta_calc_attach_flags(src, dst); ++ ++ if (!src->faked_meta) { ++ ret = kdbus_meta_proc_collect(kmsg->proc_meta, attach); ++ if (ret < 0 && !is_signal) ++ goto exit; ++ } ++ ++ ret = kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, src, attach); ++ if (ret < 0 && !is_signal) ++ goto exit; ++ ++ /* send message */ ++ ++ if (!is_signal) ++ kdbus_bus_eavesdrop(bus, src, kmsg); ++ ++ ret = kdbus_conn_entry_insert(src, dst, kmsg, wait); ++ if (ret < 0 && !is_signal) ++ goto exit; ++ ++ /* signals are treated like broadcasts, recv-errors are ignored */ ++ ret = 0; ++ ++exit: ++ up_read(&bus->name_registry->rwlock); ++ kdbus_reply_unref(wait); ++ kdbus_conn_unref(dst); ++ return ret; ++} ++ ++/** ++ * kdbus_conn_move_messages() - move messages from one connection to another ++ * @conn_dst: Connection to copy to ++ * @conn_src: Connection to copy from ++ * @name_id: Filter for the sequence number of the registered ++ * name, 0 means no filtering. ++ * ++ * Move all messages from one connection to another. This is used when ++ * an implementer connection is taking over/giving back a well-known name ++ * from/to an activator connection. ++ */ ++void kdbus_conn_move_messages(struct kdbus_conn *conn_dst, ++ struct kdbus_conn *conn_src, ++ u64 name_id) ++{ ++ struct kdbus_queue_entry *e, *e_tmp; ++ struct kdbus_reply *r, *r_tmp; ++ struct kdbus_bus *bus; ++ struct kdbus_conn *c; ++ LIST_HEAD(msg_list); ++ int i, ret = 0; ++ ++ if (WARN_ON(conn_src == conn_dst)) ++ return; ++ ++ bus = conn_src->ep->bus; ++ ++ /* lock order: domain -> bus -> ep -> names -> conn */ ++ down_read(&bus->conn_rwlock); ++ hash_for_each(bus->conn_hash, i, c, hentry) { ++ if (c == conn_src || c == conn_dst) ++ continue; ++ ++ mutex_lock(&c->lock); ++ list_for_each_entry_safe(r, r_tmp, &c->reply_list, entry) { ++ if (r->reply_src != conn_src) ++ continue; ++ ++ /* filter messages for a specific name */ ++ if (name_id > 0 && r->name_id != name_id) ++ continue; ++ ++ kdbus_conn_unref(r->reply_src); ++ r->reply_src = kdbus_conn_ref(conn_dst); ++ } ++ mutex_unlock(&c->lock); ++ } ++ up_read(&bus->conn_rwlock); ++ ++ kdbus_conn_lock2(conn_src, conn_dst); ++ list_for_each_entry_safe(e, e_tmp, &conn_src->queue.msg_list, entry) { ++ /* filter messages for a specific name */ ++ if (name_id > 0 && e->dst_name_id != name_id) ++ continue; ++ ++ if (!(conn_dst->flags & KDBUS_HELLO_ACCEPT_FD) && ++ e->msg_res && e->msg_res->fds_count > 0) { ++ kdbus_conn_lost_message(conn_dst); ++ kdbus_queue_entry_free(e); ++ continue; ++ } ++ ++ ret = kdbus_queue_entry_move(e, conn_dst); ++ if (ret < 0) { ++ kdbus_conn_lost_message(conn_dst); ++ kdbus_queue_entry_free(e); ++ continue; ++ } ++ } ++ kdbus_conn_unlock2(conn_src, conn_dst); ++ ++ /* wake up poll() */ ++ wake_up_interruptible(&conn_dst->wait); ++} ++ ++/* query the policy-database for all names of @whom */ ++static bool kdbus_conn_policy_query_all(struct kdbus_conn *conn, ++ const struct cred *conn_creds, ++ struct kdbus_policy_db *db, ++ struct kdbus_conn *whom, ++ unsigned int access) ++{ ++ struct kdbus_name_entry *ne; ++ bool pass = false; ++ int res; ++ ++ lockdep_assert_held(&conn->ep->bus->name_registry->rwlock); ++ ++ down_read(&db->entries_rwlock); ++ mutex_lock(&whom->lock); ++ ++ list_for_each_entry(ne, &whom->names_list, conn_entry) { ++ res = kdbus_policy_query_unlocked(db, conn_creds ? : conn->cred, ++ ne->name, ++ kdbus_strhash(ne->name)); ++ if (res >= (int)access) { ++ pass = true; ++ break; ++ } ++ } ++ ++ mutex_unlock(&whom->lock); ++ up_read(&db->entries_rwlock); ++ ++ return pass; ++} ++ ++/** ++ * kdbus_conn_policy_own_name() - verify a connection can own the given name ++ * @conn: Connection ++ * @conn_creds: Credentials of @conn to use for policy check ++ * @name: Name ++ * ++ * This verifies that @conn is allowed to acquire the well-known name @name. ++ * ++ * Return: true if allowed, false if not. ++ */ ++bool kdbus_conn_policy_own_name(struct kdbus_conn *conn, ++ const struct cred *conn_creds, ++ const char *name) ++{ ++ unsigned int hash = kdbus_strhash(name); ++ int res; ++ ++ if (!conn_creds) ++ conn_creds = conn->cred; ++ ++ if (conn->ep->user) { ++ res = kdbus_policy_query(&conn->ep->policy_db, conn_creds, ++ name, hash); ++ if (res < KDBUS_POLICY_OWN) ++ return false; ++ } ++ ++ if (conn->privileged) ++ return true; ++ ++ res = kdbus_policy_query(&conn->ep->bus->policy_db, conn_creds, ++ name, hash); ++ return res >= KDBUS_POLICY_OWN; ++} ++ ++/** ++ * kdbus_conn_policy_talk() - verify a connection can talk to a given peer ++ * @conn: Connection that tries to talk ++ * @conn_creds: Credentials of @conn to use for policy check ++ * @to: Connection that is talked to ++ * ++ * This verifies that @conn is allowed to talk to @to. ++ * ++ * Return: true if allowed, false if not. ++ */ ++bool kdbus_conn_policy_talk(struct kdbus_conn *conn, ++ const struct cred *conn_creds, ++ struct kdbus_conn *to) ++{ ++ if (!conn_creds) ++ conn_creds = conn->cred; ++ ++ if (conn->ep->user && ++ !kdbus_conn_policy_query_all(conn, conn_creds, &conn->ep->policy_db, ++ to, KDBUS_POLICY_TALK)) ++ return false; ++ ++ if (conn->privileged) ++ return true; ++ if (uid_eq(conn_creds->euid, to->cred->uid)) ++ return true; ++ ++ return kdbus_conn_policy_query_all(conn, conn_creds, ++ &conn->ep->bus->policy_db, to, ++ KDBUS_POLICY_TALK); ++} ++ ++/** ++ * kdbus_conn_policy_see_name_unlocked() - verify a connection can see a given ++ * name ++ * @conn: Connection ++ * @conn_creds: Credentials of @conn to use for policy check ++ * @name: Name ++ * ++ * This verifies that @conn is allowed to see the well-known name @name. Caller ++ * must hold policy-lock. ++ * ++ * Return: true if allowed, false if not. ++ */ ++bool kdbus_conn_policy_see_name_unlocked(struct kdbus_conn *conn, ++ const struct cred *conn_creds, ++ const char *name) ++{ ++ int res; ++ ++ /* ++ * By default, all names are visible on a bus. SEE policies can only be ++ * installed on custom endpoints, where by default no name is visible. ++ */ ++ if (!conn->ep->user) ++ return true; ++ ++ res = kdbus_policy_query_unlocked(&conn->ep->policy_db, ++ conn_creds ? : conn->cred, ++ name, kdbus_strhash(name)); ++ return res >= KDBUS_POLICY_SEE; ++} ++ ++static bool kdbus_conn_policy_see_name(struct kdbus_conn *conn, ++ const struct cred *conn_creds, ++ const char *name) ++{ ++ bool res; ++ ++ down_read(&conn->ep->policy_db.entries_rwlock); ++ res = kdbus_conn_policy_see_name_unlocked(conn, conn_creds, name); ++ up_read(&conn->ep->policy_db.entries_rwlock); ++ ++ return res; ++} ++ ++static bool kdbus_conn_policy_see(struct kdbus_conn *conn, ++ const struct cred *conn_creds, ++ struct kdbus_conn *whom) ++{ ++ /* ++ * By default, all names are visible on a bus, so a connection can ++ * always see other connections. SEE policies can only be installed on ++ * custom endpoints, where by default no name is visible and we hide ++ * peers from each other, unless you see at least _one_ name of the ++ * peer. ++ */ ++ return !conn->ep->user || ++ kdbus_conn_policy_query_all(conn, conn_creds, ++ &conn->ep->policy_db, whom, ++ KDBUS_POLICY_SEE); ++} ++ ++/** ++ * kdbus_conn_policy_see_notification() - verify a connection is allowed to ++ * receive a given kernel notification ++ * @conn: Connection ++ * @conn_creds: Credentials of @conn to use for policy check ++ * @kmsg: The message carrying the notification ++ * ++ * This checks whether @conn is allowed to see the kernel notification @kmsg. ++ * ++ * Return: true if allowed, false if not. ++ */ ++bool kdbus_conn_policy_see_notification(struct kdbus_conn *conn, ++ const struct cred *conn_creds, ++ const struct kdbus_kmsg *kmsg) ++{ ++ if (WARN_ON(kmsg->msg.src_id != KDBUS_SRC_ID_KERNEL)) ++ return false; ++ ++ /* ++ * Depending on the notification type, broadcasted kernel notifications ++ * have to be filtered: ++ * ++ * KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE}: This notification is forwarded ++ * to a peer if, and only if, that peer can see the name this ++ * notification is for. ++ * ++ * KDBUS_ITEM_ID_{ADD,REMOVE}: As new peers cannot have names, and all ++ * names are dropped before a peer is removed, those notifications ++ * cannot be seen on custom endpoints. Thus, we only pass them ++ * through on default endpoints. ++ */ ++ ++ switch (kmsg->notify_type) { ++ case KDBUS_ITEM_NAME_ADD: ++ case KDBUS_ITEM_NAME_REMOVE: ++ case KDBUS_ITEM_NAME_CHANGE: ++ return kdbus_conn_policy_see_name(conn, conn_creds, ++ kmsg->notify_name); ++ ++ case KDBUS_ITEM_ID_ADD: ++ case KDBUS_ITEM_ID_REMOVE: ++ return !conn->ep->user; ++ ++ default: ++ WARN(1, "Invalid type for notification broadcast: %llu\n", ++ (unsigned long long)kmsg->notify_type); ++ return false; ++ } ++} ++ ++/** ++ * kdbus_cmd_hello() - handle KDBUS_CMD_HELLO ++ * @ep: Endpoint to operate on ++ * @privileged: Whether the caller is privileged ++ * @argp: Command payload ++ * ++ * Return: Newly created connection on success, ERR_PTR on failure. ++ */ ++struct kdbus_conn *kdbus_cmd_hello(struct kdbus_ep *ep, bool privileged, ++ void __user *argp) ++{ ++ struct kdbus_cmd_hello *cmd; ++ struct kdbus_conn *c = NULL; ++ const char *item_name; ++ int ret; ++ ++ struct kdbus_arg argv[] = { ++ { .type = KDBUS_ITEM_NEGOTIATE }, ++ { .type = KDBUS_ITEM_NAME }, ++ { .type = KDBUS_ITEM_CREDS }, ++ { .type = KDBUS_ITEM_PIDS }, ++ { .type = KDBUS_ITEM_SECLABEL }, ++ { .type = KDBUS_ITEM_CONN_DESCRIPTION }, ++ { .type = KDBUS_ITEM_POLICY_ACCESS, .multiple = true }, ++ }; ++ struct kdbus_args args = { ++ .allowed_flags = KDBUS_FLAG_NEGOTIATE | ++ KDBUS_HELLO_ACCEPT_FD | ++ KDBUS_HELLO_ACTIVATOR | ++ KDBUS_HELLO_POLICY_HOLDER | ++ KDBUS_HELLO_MONITOR, ++ .argv = argv, ++ .argc = ARRAY_SIZE(argv), ++ }; ++ ++ ret = kdbus_args_parse(&args, argp, &cmd); ++ if (ret < 0) ++ return ERR_PTR(ret); ++ if (ret > 0) ++ return NULL; ++ ++ item_name = argv[1].item ? argv[1].item->str : NULL; ++ ++ c = kdbus_conn_new(ep, privileged, cmd, item_name, ++ argv[2].item ? &argv[2].item->creds : NULL, ++ argv[3].item ? &argv[3].item->pids : NULL, ++ argv[4].item ? argv[4].item->str : NULL, ++ argv[5].item ? argv[5].item->str : NULL); ++ if (IS_ERR(c)) { ++ ret = PTR_ERR(c); ++ c = NULL; ++ goto exit; ++ } ++ ++ ret = kdbus_conn_connect(c, item_name); ++ if (ret < 0) ++ goto exit; ++ ++ if (kdbus_conn_is_activator(c) || kdbus_conn_is_policy_holder(c)) { ++ ret = kdbus_conn_acquire(c); ++ if (ret < 0) ++ goto exit; ++ ++ ret = kdbus_policy_set(&c->ep->bus->policy_db, args.items, ++ args.items_size, 1, ++ kdbus_conn_is_policy_holder(c), c); ++ kdbus_conn_release(c); ++ if (ret < 0) ++ goto exit; ++ } ++ ++ if (copy_to_user(argp, cmd, sizeof(*cmd))) ++ ret = -EFAULT; ++ ++exit: ++ ret = kdbus_args_clear(&args, ret); ++ if (ret < 0) { ++ if (c) { ++ kdbus_conn_disconnect(c, false); ++ kdbus_conn_unref(c); ++ } ++ return ERR_PTR(ret); ++ } ++ return c; ++} ++ ++/** ++ * kdbus_cmd_byebye_unlocked() - handle KDBUS_CMD_BYEBYE ++ * @conn: connection to operate on ++ * @argp: command payload ++ * ++ * The caller must not hold any active reference to @conn or this will deadlock. ++ * ++ * Return: 0 on success, negative error code on failure. ++ */ ++int kdbus_cmd_byebye_unlocked(struct kdbus_conn *conn, void __user *argp) ++{ ++ struct kdbus_cmd *cmd; ++ int ret; ++ ++ struct kdbus_arg argv[] = { ++ { .type = KDBUS_ITEM_NEGOTIATE }, ++ }; ++ struct kdbus_args args = { ++ .allowed_flags = KDBUS_FLAG_NEGOTIATE, ++ .argv = argv, ++ .argc = ARRAY_SIZE(argv), ++ }; ++ ++ if (!kdbus_conn_is_ordinary(conn)) ++ return -EOPNOTSUPP; ++ ++ ret = kdbus_args_parse(&args, argp, &cmd); ++ if (ret != 0) ++ return ret; ++ ++ ret = kdbus_conn_disconnect(conn, true); ++ return kdbus_args_clear(&args, ret); ++} ++ ++/** ++ * kdbus_cmd_conn_info() - handle KDBUS_CMD_CONN_INFO ++ * @conn: connection to operate on ++ * @argp: command payload ++ * ++ * Return: 0 on success, negative error code on failure. ++ */ ++int kdbus_cmd_conn_info(struct kdbus_conn *conn, void __user *argp) ++{ ++ struct kdbus_meta_conn *conn_meta = NULL; ++ struct kdbus_pool_slice *slice = NULL; ++ struct kdbus_name_entry *entry = NULL; ++ struct kdbus_conn *owner_conn = NULL; ++ struct kdbus_info info = {}; ++ struct kdbus_cmd_info *cmd; ++ struct kdbus_bus *bus = conn->ep->bus; ++ struct kvec kvec; ++ size_t meta_size; ++ const char *name; ++ u64 attach_flags; ++ int ret; ++ ++ struct kdbus_arg argv[] = { ++ { .type = KDBUS_ITEM_NEGOTIATE }, ++ { .type = KDBUS_ITEM_NAME }, ++ }; ++ struct kdbus_args args = { ++ .allowed_flags = KDBUS_FLAG_NEGOTIATE, ++ .argv = argv, ++ .argc = ARRAY_SIZE(argv), ++ }; ++ ++ ret = kdbus_args_parse(&args, argp, &cmd); ++ if (ret != 0) ++ return ret; ++ ++ /* registry must be held throughout lookup *and* collecting data */ ++ down_read(&bus->name_registry->rwlock); ++ ++ ret = kdbus_sanitize_attach_flags(cmd->attach_flags, &attach_flags); ++ if (ret < 0) ++ goto exit; ++ ++ name = argv[1].item ? argv[1].item->str : NULL; ++ ++ if (name) { ++ entry = kdbus_name_lookup_unlocked(bus->name_registry, name); ++ if (!entry || !entry->conn || ++ !kdbus_conn_policy_see_name(conn, current_cred(), name) || ++ (cmd->id != 0 && entry->conn->id != cmd->id)) { ++ /* pretend a name doesn't exist if you cannot see it */ ++ ret = -ESRCH; ++ goto exit; ++ } ++ ++ owner_conn = kdbus_conn_ref(entry->conn); ++ } else if (cmd->id > 0) { ++ owner_conn = kdbus_bus_find_conn_by_id(bus, cmd->id); ++ if (!owner_conn || !kdbus_conn_policy_see(conn, current_cred(), ++ owner_conn)) { ++ /* pretend an id doesn't exist if you cannot see it */ ++ ret = -ENXIO; ++ goto exit; ++ } ++ } else { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ info.id = owner_conn->id; ++ info.flags = owner_conn->flags; ++ kdbus_kvec_set(&kvec, &info, sizeof(info), &info.size); ++ ++ attach_flags &= atomic64_read(&owner_conn->attach_flags_send); ++ ++ conn_meta = kdbus_meta_conn_new(); ++ if (IS_ERR(conn_meta)) { ++ ret = PTR_ERR(conn_meta); ++ conn_meta = NULL; ++ goto exit; ++ } ++ ++ ret = kdbus_meta_conn_collect(conn_meta, NULL, owner_conn, ++ attach_flags); ++ if (ret < 0) ++ goto exit; ++ ++ ret = kdbus_meta_export_prepare(owner_conn->meta, conn_meta, ++ &attach_flags, &meta_size); ++ if (ret < 0) ++ goto exit; ++ ++ slice = kdbus_pool_slice_alloc(conn->pool, ++ info.size + meta_size, false); ++ if (IS_ERR(slice)) { ++ ret = PTR_ERR(slice); ++ slice = NULL; ++ goto exit; ++ } ++ ++ ret = kdbus_meta_export(owner_conn->meta, conn_meta, attach_flags, ++ slice, sizeof(info), &meta_size); ++ if (ret < 0) ++ goto exit; ++ ++ info.size += meta_size; ++ ++ ret = kdbus_pool_slice_copy_kvec(slice, 0, &kvec, 1, sizeof(info)); ++ if (ret < 0) ++ goto exit; ++ ++ kdbus_pool_slice_publish(slice, &cmd->offset, &cmd->info_size); ++ ++ if (kdbus_member_set_user(&cmd->offset, argp, typeof(*cmd), offset) || ++ kdbus_member_set_user(&cmd->info_size, argp, ++ typeof(*cmd), info_size)) { ++ ret = -EFAULT; ++ goto exit; ++ } ++ ++ ret = 0; ++ ++exit: ++ up_read(&bus->name_registry->rwlock); ++ kdbus_pool_slice_release(slice); ++ kdbus_meta_conn_unref(conn_meta); ++ kdbus_conn_unref(owner_conn); ++ return kdbus_args_clear(&args, ret); ++} ++ ++/** ++ * kdbus_cmd_update() - handle KDBUS_CMD_UPDATE ++ * @conn: connection to operate on ++ * @argp: command payload ++ * ++ * Return: 0 on success, negative error code on failure. ++ */ ++int kdbus_cmd_update(struct kdbus_conn *conn, void __user *argp) ++{ ++ struct kdbus_bus *bus = conn->ep->bus; ++ struct kdbus_item *item_policy; ++ u64 *item_attach_send = NULL; ++ u64 *item_attach_recv = NULL; ++ struct kdbus_cmd *cmd; ++ u64 attach_send; ++ u64 attach_recv; ++ int ret; ++ ++ struct kdbus_arg argv[] = { ++ { .type = KDBUS_ITEM_NEGOTIATE }, ++ { .type = KDBUS_ITEM_ATTACH_FLAGS_SEND }, ++ { .type = KDBUS_ITEM_ATTACH_FLAGS_RECV }, ++ { .type = KDBUS_ITEM_NAME, .multiple = true }, ++ { .type = KDBUS_ITEM_POLICY_ACCESS, .multiple = true }, ++ }; ++ struct kdbus_args args = { ++ .allowed_flags = KDBUS_FLAG_NEGOTIATE, ++ .argv = argv, ++ .argc = ARRAY_SIZE(argv), ++ }; ++ ++ ret = kdbus_args_parse(&args, argp, &cmd); ++ if (ret != 0) ++ return ret; ++ ++ item_attach_send = argv[1].item ? &argv[1].item->data64[0] : NULL; ++ item_attach_recv = argv[2].item ? &argv[2].item->data64[0] : NULL; ++ item_policy = argv[3].item ? : argv[4].item; ++ ++ if (item_attach_send) { ++ if (!kdbus_conn_is_ordinary(conn) && ++ !kdbus_conn_is_monitor(conn)) { ++ ret = -EOPNOTSUPP; ++ goto exit; ++ } ++ ++ ret = kdbus_sanitize_attach_flags(*item_attach_send, ++ &attach_send); ++ if (ret < 0) ++ goto exit; ++ ++ if (bus->attach_flags_req & ~attach_send) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ } ++ ++ if (item_attach_recv) { ++ if (!kdbus_conn_is_ordinary(conn) && ++ !kdbus_conn_is_monitor(conn) && ++ !kdbus_conn_is_activator(conn)) { ++ ret = -EOPNOTSUPP; ++ goto exit; ++ } ++ ++ ret = kdbus_sanitize_attach_flags(*item_attach_recv, ++ &attach_recv); ++ if (ret < 0) ++ goto exit; ++ } ++ ++ if (item_policy && !kdbus_conn_is_policy_holder(conn)) { ++ ret = -EOPNOTSUPP; ++ goto exit; ++ } ++ ++ /* now that we verified the input, update the connection */ ++ ++ if (item_policy) { ++ ret = kdbus_policy_set(&conn->ep->bus->policy_db, cmd->items, ++ KDBUS_ITEMS_SIZE(cmd, items), ++ 1, true, conn); ++ if (ret < 0) ++ goto exit; ++ } ++ ++ if (item_attach_send) ++ atomic64_set(&conn->attach_flags_send, attach_send); ++ ++ if (item_attach_recv) ++ atomic64_set(&conn->attach_flags_recv, attach_recv); ++ ++exit: ++ return kdbus_args_clear(&args, ret); ++} ++ ++/** ++ * kdbus_cmd_send() - handle KDBUS_CMD_SEND ++ * @conn: connection to operate on ++ * @f: file this command was called on ++ * @argp: command payload ++ * ++ * Return: 0 on success, negative error code on failure. ++ */ ++int kdbus_cmd_send(struct kdbus_conn *conn, struct file *f, void __user *argp) ++{ ++ struct kdbus_cmd_send *cmd; ++ struct kdbus_kmsg *kmsg = NULL; ++ struct file *cancel_fd = NULL; ++ int ret; ++ ++ struct kdbus_arg argv[] = { ++ { .type = KDBUS_ITEM_NEGOTIATE }, ++ { .type = KDBUS_ITEM_CANCEL_FD }, ++ }; ++ struct kdbus_args args = { ++ .allowed_flags = KDBUS_FLAG_NEGOTIATE | ++ KDBUS_SEND_SYNC_REPLY, ++ .argv = argv, ++ .argc = ARRAY_SIZE(argv), ++ }; ++ ++ if (!kdbus_conn_is_ordinary(conn)) ++ return -EOPNOTSUPP; ++ ++ ret = kdbus_args_parse(&args, argp, &cmd); ++ if (ret != 0) ++ return ret; ++ ++ cmd->reply.return_flags = 0; ++ kdbus_pool_publish_empty(conn->pool, &cmd->reply.offset, ++ &cmd->reply.msg_size); ++ ++ if (argv[1].item) { ++ cancel_fd = fget(argv[1].item->fds[0]); ++ if (IS_ERR(cancel_fd)) { ++ ret = PTR_ERR(cancel_fd); ++ cancel_fd = NULL; ++ goto exit; ++ } ++ ++ if (!cancel_fd->f_op->poll) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ } ++ ++ kmsg = kdbus_kmsg_new_from_cmd(conn, cmd); ++ if (IS_ERR(kmsg)) { ++ ret = PTR_ERR(kmsg); ++ kmsg = NULL; ++ goto exit; ++ } ++ ++ if (kmsg->msg.dst_id == KDBUS_DST_ID_BROADCAST) { ++ down_read(&conn->ep->bus->name_registry->rwlock); ++ kdbus_bus_broadcast(conn->ep->bus, conn, kmsg); ++ up_read(&conn->ep->bus->name_registry->rwlock); ++ } else if (cmd->flags & KDBUS_SEND_SYNC_REPLY) { ++ struct kdbus_reply *r; ++ ktime_t exp; ++ ++ exp = ns_to_ktime(kmsg->msg.timeout_ns); ++ r = kdbus_conn_call(conn, kmsg, exp); ++ if (IS_ERR(r)) { ++ ret = PTR_ERR(r); ++ goto exit; ++ } ++ ++ ret = kdbus_conn_wait_reply(conn, cmd, f, cancel_fd, r, exp); ++ kdbus_reply_unref(r); ++ if (ret < 0) ++ goto exit; ++ } else if ((kmsg->msg.flags & KDBUS_MSG_EXPECT_REPLY) || ++ kmsg->msg.cookie_reply == 0) { ++ ret = kdbus_conn_unicast(conn, kmsg); ++ if (ret < 0) ++ goto exit; ++ } else { ++ ret = kdbus_conn_reply(conn, kmsg); ++ if (ret < 0) ++ goto exit; ++ } ++ ++ if (kdbus_member_set_user(&cmd->reply, argp, typeof(*cmd), reply)) ++ ret = -EFAULT; ++ ++exit: ++ if (cancel_fd) ++ fput(cancel_fd); ++ kdbus_kmsg_free(kmsg); ++ return kdbus_args_clear(&args, ret); ++} ++ ++/** ++ * kdbus_cmd_recv() - handle KDBUS_CMD_RECV ++ * @conn: connection to operate on ++ * @argp: command payload ++ * ++ * Return: 0 on success, negative error code on failure. ++ */ ++int kdbus_cmd_recv(struct kdbus_conn *conn, void __user *argp) ++{ ++ struct kdbus_queue_entry *entry; ++ struct kdbus_cmd_recv *cmd; ++ int ret; ++ ++ struct kdbus_arg argv[] = { ++ { .type = KDBUS_ITEM_NEGOTIATE }, ++ }; ++ struct kdbus_args args = { ++ .allowed_flags = KDBUS_FLAG_NEGOTIATE | ++ KDBUS_RECV_PEEK | ++ KDBUS_RECV_DROP | ++ KDBUS_RECV_USE_PRIORITY, ++ .argv = argv, ++ .argc = ARRAY_SIZE(argv), ++ }; ++ ++ if (!kdbus_conn_is_ordinary(conn) && ++ !kdbus_conn_is_monitor(conn) && ++ !kdbus_conn_is_activator(conn)) ++ return -EOPNOTSUPP; ++ ++ ret = kdbus_args_parse(&args, argp, &cmd); ++ if (ret != 0) ++ return ret; ++ ++ cmd->dropped_msgs = 0; ++ cmd->msg.return_flags = 0; ++ kdbus_pool_publish_empty(conn->pool, &cmd->msg.offset, ++ &cmd->msg.msg_size); ++ ++ /* DROP+priority is not realiably, so prevent it */ ++ if ((cmd->flags & KDBUS_RECV_DROP) && ++ (cmd->flags & KDBUS_RECV_USE_PRIORITY)) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ mutex_lock(&conn->lock); ++ ++ entry = kdbus_queue_peek(&conn->queue, cmd->priority, ++ cmd->flags & KDBUS_RECV_USE_PRIORITY); ++ if (!entry) { ++ mutex_unlock(&conn->lock); ++ ret = -EAGAIN; ++ } else if (cmd->flags & KDBUS_RECV_DROP) { ++ struct kdbus_reply *reply = kdbus_reply_ref(entry->reply); ++ ++ kdbus_queue_entry_free(entry); ++ ++ mutex_unlock(&conn->lock); ++ ++ if (reply) { ++ mutex_lock(&reply->reply_dst->lock); ++ if (!list_empty(&reply->entry)) { ++ kdbus_reply_unlink(reply); ++ if (reply->sync) ++ kdbus_sync_reply_wakeup(reply, -EPIPE); ++ else ++ kdbus_notify_reply_dead(conn->ep->bus, ++ reply->reply_dst->id, ++ reply->cookie); ++ } ++ mutex_unlock(&reply->reply_dst->lock); ++ kdbus_notify_flush(conn->ep->bus); ++ } ++ ++ kdbus_reply_unref(reply); ++ } else { ++ bool install_fds; ++ ++ /* ++ * PEEK just returns the location of the next message. Do not ++ * install FDs nor memfds nor anything else. The only ++ * information of interest should be the message header and ++ * metadata. Any FD numbers in the payload is undefined for ++ * PEEK'ed messages. ++ * Also make sure to never install fds into a connection that ++ * has refused to receive any. Ordinary connections will not get ++ * messages with FDs queued (the receiver will get -ECOMM), but ++ * eavesdroppers might. ++ */ ++ install_fds = (conn->flags & KDBUS_HELLO_ACCEPT_FD) && ++ !(cmd->flags & KDBUS_RECV_PEEK); ++ ++ ret = kdbus_queue_entry_install(entry, ++ &cmd->msg.return_flags, ++ install_fds); ++ if (ret < 0) { ++ mutex_unlock(&conn->lock); ++ goto exit; ++ } ++ ++ kdbus_pool_slice_publish(entry->slice, &cmd->msg.offset, ++ &cmd->msg.msg_size); ++ ++ if (!(cmd->flags & KDBUS_RECV_PEEK)) ++ kdbus_queue_entry_free(entry); ++ ++ mutex_unlock(&conn->lock); ++ } ++ ++ cmd->dropped_msgs = atomic_xchg(&conn->lost_count, 0); ++ if (cmd->dropped_msgs > 0) ++ cmd->return_flags |= KDBUS_RECV_RETURN_DROPPED_MSGS; ++ ++ if (kdbus_member_set_user(&cmd->msg, argp, typeof(*cmd), msg) || ++ kdbus_member_set_user(&cmd->dropped_msgs, argp, typeof(*cmd), ++ dropped_msgs)) ++ ret = -EFAULT; ++ ++exit: ++ return kdbus_args_clear(&args, ret); ++} ++ ++/** ++ * kdbus_cmd_free() - handle KDBUS_CMD_FREE ++ * @conn: connection to operate on ++ * @argp: command payload ++ * ++ * Return: 0 on success, negative error code on failure. ++ */ ++int kdbus_cmd_free(struct kdbus_conn *conn, void __user *argp) ++{ ++ struct kdbus_cmd_free *cmd; ++ int ret; ++ ++ struct kdbus_arg argv[] = { ++ { .type = KDBUS_ITEM_NEGOTIATE }, ++ }; ++ struct kdbus_args args = { ++ .allowed_flags = KDBUS_FLAG_NEGOTIATE, ++ .argv = argv, ++ .argc = ARRAY_SIZE(argv), ++ }; ++ ++ if (!kdbus_conn_is_ordinary(conn) && ++ !kdbus_conn_is_monitor(conn) && ++ !kdbus_conn_is_activator(conn)) ++ return -EOPNOTSUPP; ++ ++ ret = kdbus_args_parse(&args, argp, &cmd); ++ if (ret != 0) ++ return ret; ++ ++ ret = kdbus_pool_release_offset(conn->pool, cmd->offset); ++ ++ return kdbus_args_clear(&args, ret); ++} +diff --git a/ipc/kdbus/connection.h b/ipc/kdbus/connection.h +new file mode 100644 +index 000000000000..d1ffe909cb31 +--- /dev/null ++++ b/ipc/kdbus/connection.h +@@ -0,0 +1,257 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#ifndef __KDBUS_CONNECTION_H ++#define __KDBUS_CONNECTION_H ++ ++#include <linux/atomic.h> ++#include <linux/kref.h> ++#include <linux/lockdep.h> ++#include <linux/path.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_quota; ++struct kdbus_kmsg; ++ ++/** ++ * 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_send: KDBUS_ATTACH_* flags for sending ++ * @attach_flags_recv: KDBUS_ATTACH_* flags for receiving ++ * @description: Human-readable connection description, used for ++ * debugging. This field is only set when the ++ * connection is created. ++ * @ep: The endpoint this connection belongs to ++ * @lock: Connection data lock ++ * @hentry: Entry in ID <-> connection map ++ * @ep_entry: Entry in endpoint ++ * @monitor_entry: Entry in monitor, if the connection is a monitor ++ * @reply_list: List of connections this connection should ++ * reply to ++ * @work: Delayed work to handle timeouts ++ * activator for ++ * @match_db: Subscription filter to broadcast messages ++ * @meta: Active connection creator's metadata/credentials, ++ * either from the handle or from 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 ++ * @request_count: Number of pending requests issued by this ++ * connection that are waiting for replies from ++ * other peers ++ * @lost_count: Number of lost broadcast messages ++ * @wait: Wake up this endpoint ++ * @queue: The message queue associated with this connection ++ * @quota: Array of per-user quota indexed by user->id ++ * @n_quota: Number of elements in quota array ++ * @activator_of: Well-known name entry this connection acts as an ++ * @names_list: List of well-known names ++ * @names_queue_list: Well-known names this connection waits for ++ * @privileged: Whether this connection is privileged on the bus ++ * @faked_meta: Whether the metadata was faked on HELLO ++ */ ++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_send; ++ atomic64_t attach_flags_recv; ++ const char *description; ++ struct kdbus_ep *ep; ++ struct mutex lock; ++ struct hlist_node hentry; ++ struct list_head ep_entry; ++ struct list_head monitor_entry; ++ struct list_head reply_list; ++ struct delayed_work work; ++ struct kdbus_match_db *match_db; ++ struct kdbus_meta_proc *meta; ++ struct kdbus_pool *pool; ++ struct kdbus_user *user; ++ const struct cred *cred; ++ atomic_t name_count; ++ atomic_t request_count; ++ atomic_t lost_count; ++ wait_queue_head_t wait; ++ struct kdbus_queue queue; ++ ++ struct kdbus_quota *quota; ++ unsigned int n_quota; ++ ++ /* protected by registry->rwlock */ ++ struct kdbus_name_entry *activator_of; ++ struct list_head names_list; ++ struct list_head names_queue_list; ++ ++ bool privileged:1; ++ bool faked_meta:1; ++}; ++ ++struct kdbus_conn *kdbus_conn_ref(struct kdbus_conn *conn); ++struct kdbus_conn *kdbus_conn_unref(struct kdbus_conn *conn); ++bool kdbus_conn_active(const 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_has_name(struct kdbus_conn *conn, const char *name); ++int kdbus_conn_quota_inc(struct kdbus_conn *c, struct kdbus_user *u, ++ size_t memory, size_t fds); ++void kdbus_conn_quota_dec(struct kdbus_conn *c, struct kdbus_user *u, ++ size_t memory, size_t fds); ++void kdbus_conn_lost_message(struct kdbus_conn *c); ++int kdbus_conn_entry_insert(struct kdbus_conn *conn_src, ++ struct kdbus_conn *conn_dst, ++ const struct kdbus_kmsg *kmsg, ++ struct kdbus_reply *reply); ++void kdbus_conn_move_messages(struct kdbus_conn *conn_dst, ++ struct kdbus_conn *conn_src, ++ u64 name_id); ++ ++/* policy */ ++bool kdbus_conn_policy_own_name(struct kdbus_conn *conn, ++ const struct cred *conn_creds, ++ const char *name); ++bool kdbus_conn_policy_talk(struct kdbus_conn *conn, ++ const struct cred *conn_creds, ++ struct kdbus_conn *to); ++bool kdbus_conn_policy_see_name_unlocked(struct kdbus_conn *conn, ++ const struct cred *curr_creds, ++ const char *name); ++bool kdbus_conn_policy_see_notification(struct kdbus_conn *conn, ++ const struct cred *curr_creds, ++ const struct kdbus_kmsg *kmsg); ++ ++/* command dispatcher */ ++struct kdbus_conn *kdbus_cmd_hello(struct kdbus_ep *ep, bool privileged, ++ void __user *argp); ++int kdbus_cmd_byebye_unlocked(struct kdbus_conn *conn, void __user *argp); ++int kdbus_cmd_conn_info(struct kdbus_conn *conn, void __user *argp); ++int kdbus_cmd_update(struct kdbus_conn *conn, void __user *argp); ++int kdbus_cmd_send(struct kdbus_conn *conn, struct file *f, void __user *argp); ++int kdbus_cmd_recv(struct kdbus_conn *conn, void __user *argp); ++int kdbus_cmd_free(struct kdbus_conn *conn, void __user *argp); ++ ++/** ++ * kdbus_conn_is_ordinary() - 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_ordinary(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; ++} ++ ++/** ++ * kdbus_conn_lock2() - Lock two connections ++ * @a: connection A to lock or NULL ++ * @b: connection B to lock or NULL ++ * ++ * Lock two connections at once. As we need to have a stable locking order, we ++ * always lock the connection with lower memory address first. ++ */ ++static inline void kdbus_conn_lock2(struct kdbus_conn *a, struct kdbus_conn *b) ++{ ++ if (a < b) { ++ if (a) ++ mutex_lock(&a->lock); ++ if (b && b != a) ++ mutex_lock_nested(&b->lock, !!a); ++ } else { ++ if (b) ++ mutex_lock(&b->lock); ++ if (a && a != b) ++ mutex_lock_nested(&a->lock, !!b); ++ } ++} ++ ++/** ++ * kdbus_conn_unlock2() - Unlock two connections ++ * @a: connection A to unlock or NULL ++ * @b: connection B to unlock or NULL ++ * ++ * Unlock two connections at once. See kdbus_conn_lock2(). ++ */ ++static inline void kdbus_conn_unlock2(struct kdbus_conn *a, ++ struct kdbus_conn *b) ++{ ++ if (a) ++ mutex_unlock(&a->lock); ++ if (b && b != a) ++ mutex_unlock(&b->lock); ++} ++ ++/** ++ * kdbus_conn_assert_active() - lockdep assert on active lock ++ * @conn: connection that shall be active ++ * ++ * This verifies via lockdep that the caller holds an active reference to the ++ * given connection. ++ */ ++static inline void kdbus_conn_assert_active(struct kdbus_conn *conn) ++{ ++ lockdep_assert_held(conn); ++} ++ ++#endif +diff --git a/ipc/kdbus/item.c b/ipc/kdbus/item.c +new file mode 100644 +index 000000000000..745ad5495096 +--- /dev/null ++++ b/ipc/kdbus/item.c +@@ -0,0 +1,339 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#include <linux/ctype.h> ++#include <linux/fs.h> ++#include <linux/string.h> ++ ++#include "item.h" ++#include "limits.h" ++#include "util.h" ++ ++/* ++ * 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. ++ */ ++static bool kdbus_str_valid(const char *str, size_t size) ++{ ++ return size > 0 && memchr(str, '\0', size) == str + size - 1; ++} ++ ++/** ++ * 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) ++{ ++ const char *name = item->str; ++ unsigned int i; ++ size_t len; ++ ++ 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(name, KDBUS_ITEM_PAYLOAD_SIZE(item))) ++ return -EINVAL; ++ ++ 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 && (name[i] == '-' || name[i] == '.')) ++ continue; ++ ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * kdbus_item_validate() - validate a single item ++ * @item: item to validate ++ * ++ * Return: 0 if item is valid, negative error code if not. ++ */ ++int kdbus_item_validate(const struct kdbus_item *item) ++{ ++ size_t payload_size = KDBUS_ITEM_PAYLOAD_SIZE(item); ++ size_t l; ++ int ret; ++ ++ BUILD_BUG_ON(KDBUS_ITEM_HEADER_SIZE != ++ sizeof(struct kdbus_item_header)); ++ ++ if (item->size < KDBUS_ITEM_HEADER_SIZE) ++ return -EINVAL; ++ ++ switch (item->type) { ++ case KDBUS_ITEM_NEGOTIATE: ++ if (payload_size % sizeof(u64) != 0) ++ return -EINVAL; ++ break; ++ ++ 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_CANCEL_FD: ++ if (payload_size != sizeof(int)) ++ 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_DESCRIPTION: ++ case KDBUS_ITEM_MAKE_NAME: ++ ret = kdbus_item_validate_name(item); ++ if (ret < 0) ++ return ret; ++ break; ++ ++ case KDBUS_ITEM_ATTACH_FLAGS_SEND: ++ case KDBUS_ITEM_ATTACH_FLAGS_RECV: ++ 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(u32) != 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: ++ if (payload_size < sizeof(u32)) ++ return -EINVAL; ++ if (payload_size < sizeof(u32) + ++ 4 * CAP_TO_INDEX(item->caps.last_cap) * sizeof(u32)) ++ return -EINVAL; ++ 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; ++} ++ ++static struct kdbus_item *kdbus_items_get(const struct kdbus_item *items, ++ size_t items_size, ++ unsigned int item_type) ++{ ++ const struct kdbus_item *iter, *found = NULL; ++ ++ KDBUS_ITEMS_FOREACH(iter, items, items_size) { ++ if (iter->type == item_type) { ++ if (found) ++ return ERR_PTR(-EEXIST); ++ found = iter; ++ } ++ } ++ ++ return (struct kdbus_item *)found ? : ERR_PTR(-EBADMSG); ++} ++ ++/** ++ * 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 ++ * ++ * 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: the string, if the item was found exactly once, ERR_PTR(-EEXIST) ++ * if the item was found more than once, and ERR_PTR(-EBADMSG) if there was ++ * no item of the given type. ++ */ ++const char *kdbus_items_get_str(const struct kdbus_item *items, ++ size_t items_size, ++ unsigned int item_type) ++{ ++ const struct kdbus_item *item; ++ ++ item = kdbus_items_get(items, items_size, item_type); ++ return IS_ERR(item) ? ERR_CAST(item) : item->str; ++} ++ ++/** ++ * kdbus_item_set() - Set item content ++ * @item: The item to modify ++ * @type: The item type to set (KDBUS_ITEM_*) ++ * @data: Data to copy to item->data, may be %NULL ++ * @len: Number of bytes in @data ++ * ++ * This sets type, size and data fields of an item. If @data is NULL, the data ++ * memory is cleared. ++ * ++ * Note that you must align your @data memory to 8 bytes. Trailing padding (in ++ * case @len is not 8byte aligned) is cleared by this call. ++ * ++ * Returns: Pointer to the following item. ++ */ ++struct kdbus_item *kdbus_item_set(struct kdbus_item *item, u64 type, ++ const void *data, size_t len) ++{ ++ item->type = type; ++ item->size = KDBUS_ITEM_HEADER_SIZE + len; ++ ++ if (data) { ++ memcpy(item->data, data, len); ++ memset(item->data + len, 0, KDBUS_ALIGN8(len) - len); ++ } else { ++ memset(item->data, 0, KDBUS_ALIGN8(len)); ++ } ++ ++ return KDBUS_ITEM_NEXT(item); ++} +diff --git a/ipc/kdbus/item.h b/ipc/kdbus/item.h +new file mode 100644 +index 000000000000..eeefd8beac3b +--- /dev/null ++++ b/ipc/kdbus/item.h +@@ -0,0 +1,64 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> ++ * ++ * 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_NEXT(_i) (typeof(_i))(((u8 *)_i) + KDBUS_ALIGN8((_i)->size)) ++#define KDBUS_ITEMS_SIZE(_h, _is) ((_h)->size - offsetof(typeof(*_h), _is)) ++#define KDBUS_ITEM_HEADER_SIZE offsetof(struct kdbus_item, data) ++#define KDBUS_ITEM_SIZE(_s) KDBUS_ALIGN8(KDBUS_ITEM_HEADER_SIZE + (_s)) ++#define KDBUS_ITEM_PAYLOAD_SIZE(_i) ((_i)->size - KDBUS_ITEM_HEADER_SIZE) ++ ++#define KDBUS_ITEMS_FOREACH(_i, _is, _s) \ ++ for (_i = _is; \ ++ ((u8 *)(_i) < (u8 *)(_is) + (_s)) && \ ++ ((u8 *)(_i) >= (u8 *)(_is)); \ ++ _i = KDBUS_ITEM_NEXT(_i)) ++ ++#define KDBUS_ITEM_VALID(_i, _is, _s) \ ++ ((_i)->size >= KDBUS_ITEM_HEADER_SIZE && \ ++ (u8 *)(_i) + (_i)->size > (u8 *)(_i) && \ ++ (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))) ++ ++/** ++ * struct kdbus_item_header - Describes the fix part of an item ++ * @size: The total size of the item ++ * @type: The item type, one of KDBUS_ITEM_* ++ */ ++struct kdbus_item_header { ++ u64 size; ++ u64 type; ++}; ++ ++int kdbus_item_validate_name(const struct kdbus_item *item); ++int kdbus_item_validate(const struct kdbus_item *item); ++int kdbus_items_validate(const struct kdbus_item *items, size_t items_size); ++const char *kdbus_items_get_str(const struct kdbus_item *items, ++ size_t items_size, ++ unsigned int item_type); ++struct kdbus_item *kdbus_item_set(struct kdbus_item *item, u64 type, ++ const void *data, size_t len); ++ ++#endif +diff --git a/ipc/kdbus/message.c b/ipc/kdbus/message.c +new file mode 100644 +index 000000000000..80960756a329 +--- /dev/null ++++ b/ipc/kdbus/message.c +@@ -0,0 +1,616 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> ++ * ++ * 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/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) ++ ++static struct kdbus_msg_resources *kdbus_msg_resources_new(void) ++{ ++ struct kdbus_msg_resources *r; ++ ++ r = kzalloc(sizeof(*r), GFP_KERNEL); ++ if (!r) ++ return ERR_PTR(-ENOMEM); ++ ++ kref_init(&r->kref); ++ ++ return r; ++} ++ ++static void __kdbus_msg_resources_free(struct kref *kref) ++{ ++ struct kdbus_msg_resources *r = ++ container_of(kref, struct kdbus_msg_resources, kref); ++ size_t i; ++ ++ for (i = 0; i < r->data_count; ++i) { ++ switch (r->data[i].type) { ++ case KDBUS_MSG_DATA_VEC: ++ /* nothing to do */ ++ break; ++ case KDBUS_MSG_DATA_MEMFD: ++ if (r->data[i].memfd.file) ++ fput(r->data[i].memfd.file); ++ break; ++ } ++ } ++ ++ for (i = 0; i < r->fds_count; i++) ++ if (r->fds[i]) ++ fput(r->fds[i]); ++ ++ kfree(r->dst_name); ++ kfree(r->data); ++ kfree(r->fds); ++ kfree(r); ++} ++ ++/** ++ * kdbus_msg_resources_ref() - Acquire reference to msg resources ++ * @r: resources to acquire ref to ++ * ++ * Return: The acquired resource ++ */ ++struct kdbus_msg_resources * ++kdbus_msg_resources_ref(struct kdbus_msg_resources *r) ++{ ++ if (r) ++ kref_get(&r->kref); ++ return r; ++} ++ ++/** ++ * kdbus_msg_resources_unref() - Drop reference to msg resources ++ * @r: resources to drop reference of ++ * ++ * Return: NULL ++ */ ++struct kdbus_msg_resources * ++kdbus_msg_resources_unref(struct kdbus_msg_resources *r) ++{ ++ if (r) ++ kref_put(&r->kref, __kdbus_msg_resources_free); ++ return NULL; ++} ++ ++/** ++ * kdbus_kmsg_free() - free allocated message ++ * @kmsg: Message ++ */ ++void kdbus_kmsg_free(struct kdbus_kmsg *kmsg) ++{ ++ if (!kmsg) ++ return; ++ ++ kdbus_msg_resources_unref(kmsg->res); ++ kdbus_meta_conn_unref(kmsg->conn_meta); ++ kdbus_meta_proc_unref(kmsg->proc_meta); ++ kfree(kmsg->iov); ++ kfree(kmsg); ++} ++ ++/** ++ * kdbus_kmsg_new() - allocate message ++ * @bus: Bus this message is allocated on ++ * @extra_size: Additional size to reserve for data ++ * ++ * Return: new kdbus_kmsg on success, ERR_PTR on failure. ++ */ ++struct kdbus_kmsg *kdbus_kmsg_new(struct kdbus_bus *bus, size_t extra_size) ++{ ++ struct kdbus_kmsg *m; ++ size_t size; ++ int ret; ++ ++ size = sizeof(struct kdbus_kmsg) + KDBUS_ITEM_SIZE(extra_size); ++ m = kzalloc(size, GFP_KERNEL); ++ if (!m) ++ return ERR_PTR(-ENOMEM); ++ ++ m->seq = atomic64_inc_return(&bus->domain->last_id); ++ m->msg.size = size - KDBUS_KMSG_HEADER_SIZE; ++ m->msg.items[0].size = KDBUS_ITEM_SIZE(extra_size); ++ ++ m->proc_meta = kdbus_meta_proc_new(); ++ if (IS_ERR(m->proc_meta)) { ++ ret = PTR_ERR(m->proc_meta); ++ m->proc_meta = NULL; ++ goto exit; ++ } ++ ++ m->conn_meta = kdbus_meta_conn_new(); ++ if (IS_ERR(m->conn_meta)) { ++ ret = PTR_ERR(m->conn_meta); ++ m->conn_meta = NULL; ++ goto exit; ++ } ++ ++ return m; ++ ++exit: ++ kdbus_kmsg_free(m); ++ return ERR_PTR(ret); ++} ++ ++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; ++} ++ ++static const char * const zeros = "\0\0\0\0\0\0\0"; ++ ++/* ++ * kdbus_msg_scan_items() - validate incoming data and prepare parsing ++ * @kmsg: Message ++ * @bus: Bus the message is sent over ++ * ++ * Return: 0 on success, negative errno on failure. ++ * ++ * Files references in MEMFD or FDS items are pinned. ++ * ++ * On errors, the caller should drop any taken reference with ++ * kdbus_kmsg_free() ++ */ ++static int kdbus_msg_scan_items(struct kdbus_kmsg *kmsg, ++ struct kdbus_bus *bus) ++{ ++ struct kdbus_msg_resources *res = kmsg->res; ++ const struct kdbus_msg *msg = &kmsg->msg; ++ const struct kdbus_item *item; ++ size_t n, n_vecs, n_memfds; ++ bool has_bloom = false; ++ bool has_name = false; ++ bool has_fds = false; ++ bool is_broadcast; ++ bool is_signal; ++ u64 vec_size; ++ ++ is_broadcast = (msg->dst_id == KDBUS_DST_ID_BROADCAST); ++ is_signal = !!(msg->flags & KDBUS_MSG_SIGNAL); ++ ++ /* count data payloads */ ++ n_vecs = 0; ++ n_memfds = 0; ++ KDBUS_ITEMS_FOREACH(item, msg->items, KDBUS_ITEMS_SIZE(msg, items)) { ++ switch (item->type) { ++ case KDBUS_ITEM_PAYLOAD_VEC: ++ ++n_vecs; ++ break; ++ case KDBUS_ITEM_PAYLOAD_MEMFD: ++ ++n_memfds; ++ if (item->memfd.size % 8) ++ ++n_vecs; ++ break; ++ default: ++ break; ++ } ++ } ++ ++ n = n_vecs + n_memfds; ++ if (n > 0) { ++ res->data = kcalloc(n, sizeof(*res->data), GFP_KERNEL); ++ if (!res->data) ++ return -ENOMEM; ++ } ++ ++ if (n_vecs > 0) { ++ kmsg->iov = kcalloc(n_vecs, sizeof(*kmsg->iov), GFP_KERNEL); ++ if (!kmsg->iov) ++ return -ENOMEM; ++ } ++ ++ /* import data payloads */ ++ n = 0; ++ vec_size = 0; ++ KDBUS_ITEMS_FOREACH(item, msg->items, KDBUS_ITEMS_SIZE(msg, items)) { ++ size_t payload_size = KDBUS_ITEM_PAYLOAD_SIZE(item); ++ struct iovec *iov = kmsg->iov + kmsg->iov_count; ++ ++ if (++n > KDBUS_MSG_MAX_ITEMS) ++ return -E2BIG; ++ ++ switch (item->type) { ++ case KDBUS_ITEM_PAYLOAD_VEC: { ++ struct kdbus_msg_data *d = res->data + res->data_count; ++ void __force __user *ptr = KDBUS_PTR(item->vec.address); ++ size_t size = item->vec.size; ++ ++ if (vec_size + size < vec_size) ++ return -EMSGSIZE; ++ if (vec_size + size > KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE) ++ return -EMSGSIZE; ++ ++ d->type = KDBUS_MSG_DATA_VEC; ++ d->size = size; ++ ++ if (ptr) { ++ if (unlikely(!access_ok(VERIFY_READ, ptr, ++ size))) ++ return -EFAULT; ++ ++ d->vec.off = kmsg->pool_size; ++ iov->iov_base = ptr; ++ iov->iov_len = size; ++ } else { ++ d->vec.off = ~0ULL; ++ iov->iov_base = (char __user *)zeros; ++ iov->iov_len = size % 8; ++ } ++ ++ if (kmsg->pool_size + iov->iov_len < kmsg->pool_size) ++ return -EMSGSIZE; ++ ++ kmsg->pool_size += iov->iov_len; ++ ++kmsg->iov_count; ++ ++res->vec_count; ++ ++res->data_count; ++ vec_size += size; ++ ++ break; ++ } ++ ++ case KDBUS_ITEM_PAYLOAD_MEMFD: { ++ struct kdbus_msg_data *d = res->data + res->data_count; ++ u64 start = item->memfd.start; ++ u64 size = item->memfd.size; ++ size_t pad = size % 8; ++ int seals, mask; ++ struct file *f; ++ ++ if (kmsg->pool_size + size % 8 < kmsg->pool_size) ++ return -EMSGSIZE; ++ if (start + size < start) ++ return -EMSGSIZE; ++ ++ if (item->memfd.fd < 0) ++ return -EBADF; ++ ++ if (res->memfd_count >= KDBUS_MSG_MAX_MEMFD_ITEMS) ++ return -E2BIG; ++ ++ f = fget(item->memfd.fd); ++ if (!f) ++ return -EBADF; ++ ++ if (pad) { ++ iov->iov_base = (char __user *)zeros; ++ iov->iov_len = pad; ++ ++ kmsg->pool_size += pad; ++ ++kmsg->iov_count; ++ } ++ ++ ++res->data_count; ++ ++res->memfd_count; ++ ++ d->type = KDBUS_MSG_DATA_MEMFD; ++ d->size = size; ++ d->memfd.start = start; ++ d->memfd.file = f; ++ ++ /* ++ * 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 | F_SEAL_SEAL; ++ if ((seals & mask) != mask) ++ return -ETXTBSY; ++ ++ if (start + size > (u64)i_size_read(file_inode(f))) ++ return -EBADF; ++ ++ break; ++ } ++ ++ case KDBUS_ITEM_FDS: { ++ unsigned int i; ++ unsigned int fds_count = payload_size / sizeof(int); ++ ++ /* do not allow multiple fd arrays */ ++ if (has_fds) ++ return -EEXIST; ++ has_fds = true; ++ ++ /* Do not allow to broadcast file descriptors */ ++ if (is_broadcast) ++ return -ENOTUNIQ; ++ ++ if (fds_count > KDBUS_CONN_MAX_FDS_PER_USER) ++ return -EMFILE; ++ ++ res->fds = kcalloc(fds_count, sizeof(struct file *), ++ GFP_KERNEL); ++ if (!res->fds) ++ return -ENOMEM; ++ ++ for (i = 0; i < fds_count; i++) { ++ int fd = item->fds[i]; ++ int ret; ++ ++ /* ++ * Verify the fd and increment the usage count. ++ * Use fget_raw() to allow passing O_PATH fds. ++ */ ++ if (fd < 0) ++ return -EBADF; ++ ++ res->fds[i] = fget_raw(fd); ++ if (!res->fds[i]) ++ return -EBADF; ++ ++ res->fds_count++; ++ ++ ret = kdbus_handle_check_file(res->fds[i]); ++ 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_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 != 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; ++ ++ res->dst_name = kstrdup(item->str, GFP_KERNEL); ++ if (!res->dst_name) ++ return -ENOMEM; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ /* name is needed if no ID is given */ ++ if (msg->dst_id == KDBUS_DST_ID_NAME && !has_name) ++ return -EDESTADDRREQ; ++ ++ if (is_broadcast) { ++ /* Broadcasts can't take names */ ++ if (has_name) ++ return -EBADMSG; ++ ++ /* All broadcasts have to be signals */ ++ if (!is_signal) ++ return -EBADMSG; ++ ++ /* Timeouts are not allowed for broadcasts */ ++ if (msg->timeout_ns > 0) ++ return -ENOTUNIQ; ++ } ++ ++ /* ++ * Signal messages require a bloom filter, and bloom filters are ++ * only valid with signals. ++ */ ++ if (is_signal ^ has_bloom) ++ return -EBADMSG; ++ ++ return 0; ++} ++ ++/** ++ * kdbus_kmsg_new_from_cmd() - create kernel message from send payload ++ * @conn: Connection ++ * @cmd_send: Payload of KDBUS_CMD_SEND ++ * ++ * Return: a new kdbus_kmsg on success, ERR_PTR on failure. ++ */ ++struct kdbus_kmsg *kdbus_kmsg_new_from_cmd(struct kdbus_conn *conn, ++ struct kdbus_cmd_send *cmd_send) ++{ ++ struct kdbus_kmsg *m; ++ u64 size; ++ int ret; ++ ++ ret = kdbus_copy_from_user(&size, KDBUS_PTR(cmd_send->msg_address), ++ sizeof(size)); ++ if (ret < 0) ++ return ERR_PTR(ret); ++ ++ if (size < sizeof(struct kdbus_msg) || size > KDBUS_MSG_MAX_SIZE) ++ return ERR_PTR(-EINVAL); ++ ++ m = kmalloc(size + KDBUS_KMSG_HEADER_SIZE, GFP_KERNEL); ++ if (!m) ++ return ERR_PTR(-ENOMEM); ++ ++ memset(m, 0, KDBUS_KMSG_HEADER_SIZE); ++ m->seq = atomic64_inc_return(&conn->ep->bus->domain->last_id); ++ ++ m->proc_meta = kdbus_meta_proc_new(); ++ if (IS_ERR(m->proc_meta)) { ++ ret = PTR_ERR(m->proc_meta); ++ m->proc_meta = NULL; ++ goto exit_free; ++ } ++ ++ m->conn_meta = kdbus_meta_conn_new(); ++ if (IS_ERR(m->conn_meta)) { ++ ret = PTR_ERR(m->conn_meta); ++ m->conn_meta = NULL; ++ goto exit_free; ++ } ++ ++ if (copy_from_user(&m->msg, KDBUS_PTR(cmd_send->msg_address), size)) { ++ ret = -EFAULT; ++ goto exit_free; ++ } ++ ++ if (m->msg.size != size) { ++ ret = -EINVAL; ++ goto exit_free; ++ } ++ ++ if (m->msg.flags & ~(KDBUS_MSG_EXPECT_REPLY | ++ KDBUS_MSG_NO_AUTO_START | ++ KDBUS_MSG_SIGNAL)) { ++ ret = -EINVAL; ++ goto exit_free; ++ } ++ ++ ret = kdbus_items_validate(m->msg.items, ++ KDBUS_ITEMS_SIZE(&m->msg, items)); ++ if (ret < 0) ++ goto exit_free; ++ ++ m->res = kdbus_msg_resources_new(); ++ if (IS_ERR(m->res)) { ++ ret = PTR_ERR(m->res); ++ m->res = NULL; ++ goto exit_free; ++ } ++ ++ /* do not accept kernel-generated messages */ ++ if (m->msg.payload_type == KDBUS_PAYLOAD_KERNEL) { ++ ret = -EINVAL; ++ goto exit_free; ++ } ++ ++ if (m->msg.flags & KDBUS_MSG_EXPECT_REPLY) { ++ /* requests for replies need timeout and cookie */ ++ if (m->msg.timeout_ns == 0 || m->msg.cookie == 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; ++ } ++ ++ /* replies may not be expected for signals */ ++ if (m->msg.flags & KDBUS_MSG_SIGNAL) { ++ ret = -EINVAL; ++ goto exit_free; ++ } ++ } else { ++ /* ++ * KDBUS_SEND_SYNC_REPLY is only valid together with ++ * KDBUS_MSG_EXPECT_REPLY ++ */ ++ if (cmd_send->flags & KDBUS_SEND_SYNC_REPLY) { ++ ret = -EINVAL; ++ goto exit_free; ++ } ++ ++ /* replies cannot be signals */ ++ if (m->msg.cookie_reply && (m->msg.flags & KDBUS_MSG_SIGNAL)) { ++ ret = -EINVAL; ++ goto exit_free; ++ } ++ } ++ ++ ret = kdbus_msg_scan_items(m, conn->ep->bus); ++ 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; ++ ++ return m; ++ ++exit_free: ++ kdbus_kmsg_free(m); ++ return ERR_PTR(ret); ++} +diff --git a/ipc/kdbus/message.h b/ipc/kdbus/message.h +new file mode 100644 +index 000000000000..af4775850235 +--- /dev/null ++++ b/ipc/kdbus/message.h +@@ -0,0 +1,133 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#ifndef __KDBUS_MESSAGE_H ++#define __KDBUS_MESSAGE_H ++ ++#include "util.h" ++#include "metadata.h" ++ ++/** ++ * enum kdbus_msg_data_type - Type of kdbus_msg_data payloads ++ * @KDBUS_MSG_DATA_VEC: Data vector provided by user-space ++ * @KDBUS_MSG_DATA_MEMFD: Memfd payload ++ */ ++enum kdbus_msg_data_type { ++ KDBUS_MSG_DATA_VEC, ++ KDBUS_MSG_DATA_MEMFD, ++}; ++ ++/** ++ * struct kdbus_msg_data - Data payload as stored by messages ++ * @type: Type of payload (KDBUS_MSG_DATA_*) ++ * @size: Size of the described payload ++ * @off: The offset, relative to the vec slice ++ * @start: Offset inside the memfd ++ * @file: Backing file referenced by the memfd ++ */ ++struct kdbus_msg_data { ++ unsigned int type; ++ u64 size; ++ ++ union { ++ struct { ++ u64 off; ++ } vec; ++ struct { ++ u64 start; ++ struct file *file; ++ } memfd; ++ }; ++}; ++ ++/** ++ * struct kdbus_kmsg_resources - resources of a message ++ * @kref: Reference counter ++ * @dst_name: Short-cut to msg for faster lookup ++ * @fds: Array of file descriptors to pass ++ * @fds_count: Number of file descriptors to pass ++ * @data: Array of data payloads ++ * @vec_count: Number of VEC entries ++ * @memfd_count: Number of MEMFD entries in @data ++ * @data_count: Sum of @vec_count + @memfd_count ++ */ ++struct kdbus_msg_resources { ++ struct kref kref; ++ const char *dst_name; ++ ++ struct file **fds; ++ unsigned int fds_count; ++ ++ struct kdbus_msg_data *data; ++ size_t vec_count; ++ size_t memfd_count; ++ size_t data_count; ++}; ++ ++struct kdbus_msg_resources * ++kdbus_msg_resources_ref(struct kdbus_msg_resources *r); ++struct kdbus_msg_resources * ++kdbus_msg_resources_unref(struct kdbus_msg_resources *r); ++ ++/** ++ * 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_id: Short-cut to msg for faster lookup ++ * @bloom_filter: Bloom filter to match message properties ++ * @bloom_generation: Generation of bloom element set ++ * @notify_entry: List of kernel-generated notifications ++ * @iov: Array of iovec, describing the payload to copy ++ * @iov_count: Number of array members in @iov ++ * @pool_size: Overall size of inlined data referenced by @iov ++ * @proc_meta: Appended SCM-like metadata of the sending process ++ * @conn_meta: Appended SCM-like metadata of the sending connection ++ * @res: Message resources ++ * @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; ++ ++ u64 dst_name_id; ++ const struct kdbus_bloom_filter *bloom_filter; ++ u64 bloom_generation; ++ struct list_head notify_entry; ++ ++ struct iovec *iov; ++ size_t iov_count; ++ u64 pool_size; ++ ++ struct kdbus_meta_proc *proc_meta; ++ struct kdbus_meta_conn *conn_meta; ++ struct kdbus_msg_resources *res; ++ ++ /* variable size, must be the last member */ ++ struct kdbus_msg msg; ++}; ++ ++struct kdbus_bus; ++struct kdbus_conn; ++ ++struct kdbus_kmsg *kdbus_kmsg_new(struct kdbus_bus *bus, size_t extra_size); ++struct kdbus_kmsg *kdbus_kmsg_new_from_cmd(struct kdbus_conn *conn, ++ struct kdbus_cmd_send *cmd_send); ++void kdbus_kmsg_free(struct kdbus_kmsg *kmsg); ++ ++#endif +diff --git a/ipc/kdbus/queue.c b/ipc/kdbus/queue.c +new file mode 100644 +index 000000000000..a449464a3975 +--- /dev/null ++++ b/ipc/kdbus/queue.c +@@ -0,0 +1,678 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#include <linux/audit.h> ++#include <linux/file.h> ++#include <linux/fs.h> ++#include <linux/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 <linux/uio.h> ++ ++#include "util.h" ++#include "domain.h" ++#include "connection.h" ++#include "item.h" ++#include "message.h" ++#include "metadata.h" ++#include "queue.h" ++#include "reply.h" ++ ++/** ++ * 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; ++} ++ ++/** ++ * kdbus_queue_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 ++ * ++ * 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: the peeked queue entry on success, NULL if no suitable msg is found ++ */ ++struct kdbus_queue_entry *kdbus_queue_peek(struct kdbus_queue *queue, ++ s64 priority, bool use_priority) ++{ ++ struct kdbus_queue_entry *e; ++ ++ if (list_empty(&queue->msg_list)) ++ return NULL; ++ ++ 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 NULL; ++ } else { ++ /* ignore the priority, return the next entry in the entry */ ++ e = list_first_entry(&queue->msg_list, ++ struct kdbus_queue_entry, entry); ++ } ++ ++ return e; ++} ++ ++static void kdbus_queue_entry_link(struct kdbus_queue_entry *entry) ++{ ++ struct kdbus_queue *queue = &entry->conn->queue; ++ struct rb_node **n, *pn = NULL; ++ bool highest = true; ++ ++ lockdep_assert_held(&entry->conn->lock); ++ if (WARN_ON(!list_empty(&entry->entry))) ++ return; ++ ++ /* 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); ++} ++ ++static void kdbus_queue_entry_unlink(struct kdbus_queue_entry *entry) ++{ ++ struct kdbus_queue *queue = &entry->conn->queue; ++ ++ lockdep_assert_held(&entry->conn->lock); ++ if (list_empty(&entry->entry)) ++ return; ++ ++ list_del_init(&entry->entry); ++ ++ 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_new() - allocate a queue entry ++ * @conn_dst: destination connection ++ * @kmsg: kmsg object the queue entry should track ++ * @user: user to account message on (or NULL for kernel messages) ++ * ++ * 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. ++ * ++ * Return: the allocated entry on success, or an ERR_PTR on failures. ++ */ ++struct kdbus_queue_entry *kdbus_queue_entry_new(struct kdbus_conn *conn_dst, ++ const struct kdbus_kmsg *kmsg, ++ struct kdbus_user *user) ++{ ++ struct kdbus_msg_resources *res = kmsg->res; ++ const struct kdbus_msg *msg = &kmsg->msg; ++ struct kdbus_queue_entry *entry; ++ size_t memfd_cnt = 0; ++ struct kvec kvec[2]; ++ size_t meta_size; ++ size_t msg_size; ++ u64 payload_off; ++ u64 size = 0; ++ int ret = 0; ++ ++ entry = kzalloc(sizeof(*entry), GFP_KERNEL); ++ if (!entry) ++ return ERR_PTR(-ENOMEM); ++ ++ INIT_LIST_HEAD(&entry->entry); ++ entry->priority = msg->priority; ++ entry->dst_name_id = kmsg->dst_name_id; ++ entry->msg_res = kdbus_msg_resources_ref(res); ++ entry->proc_meta = kdbus_meta_proc_ref(kmsg->proc_meta); ++ entry->conn_meta = kdbus_meta_conn_ref(kmsg->conn_meta); ++ entry->conn = kdbus_conn_ref(conn_dst); ++ ++ if (kmsg->msg.src_id == KDBUS_SRC_ID_KERNEL) ++ msg_size = msg->size; ++ else ++ msg_size = offsetof(struct kdbus_msg, items); ++ ++ /* sum up the size of the needed slice */ ++ size = msg_size; ++ ++ if (res) { ++ size += res->vec_count * ++ KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec)); ++ ++ if (res->memfd_count) { ++ entry->memfd_offset = ++ kcalloc(res->memfd_count, sizeof(size_t), ++ GFP_KERNEL); ++ if (!entry->memfd_offset) { ++ ret = -ENOMEM; ++ goto exit_free_entry; ++ } ++ ++ size += res->memfd_count * ++ KDBUS_ITEM_SIZE(sizeof(struct kdbus_memfd)); ++ } ++ ++ if (res->fds_count) ++ size += KDBUS_ITEM_SIZE(sizeof(int) * res->fds_count); ++ ++ if (res->dst_name) ++ size += KDBUS_ITEM_SIZE(strlen(res->dst_name) + 1); ++ } ++ ++ /* ++ * Remember the offset of the metadata part, so we can override ++ * this part later during kdbus_queue_entry_install(). ++ */ ++ entry->meta_offset = size; ++ ++ if (entry->proc_meta || entry->conn_meta) { ++ entry->attach_flags = ++ atomic64_read(&conn_dst->attach_flags_recv); ++ ++ ret = kdbus_meta_export_prepare(entry->proc_meta, ++ entry->conn_meta, ++ &entry->attach_flags, ++ &meta_size); ++ if (ret < 0) ++ goto exit_free_entry; ++ ++ size += meta_size; ++ } ++ ++ payload_off = size; ++ size += kmsg->pool_size; ++ size = KDBUS_ALIGN8(size); ++ ++ ret = kdbus_conn_quota_inc(conn_dst, user, size, ++ res ? res->fds_count : 0); ++ if (ret < 0) ++ goto exit_free_entry; ++ ++ entry->slice = kdbus_pool_slice_alloc(conn_dst->pool, size, true); ++ if (IS_ERR(entry->slice)) { ++ ret = PTR_ERR(entry->slice); ++ entry->slice = NULL; ++ kdbus_conn_quota_dec(conn_dst, user, size, ++ res ? res->fds_count : 0); ++ goto exit_free_entry; ++ } ++ ++ /* we accounted for exactly 'size' bytes, make sure it didn't grow */ ++ WARN_ON(kdbus_pool_slice_size(entry->slice) != size); ++ entry->user = kdbus_user_ref(user); ++ ++ /* copy message header */ ++ kvec[0].iov_base = (char *)msg; ++ kvec[0].iov_len = msg_size; ++ ++ ret = kdbus_pool_slice_copy_kvec(entry->slice, 0, kvec, 1, msg_size); ++ if (ret < 0) ++ goto exit_free_entry; ++ ++ /* 'size' will now track the write position */ ++ size = msg_size; ++ ++ /* create message payload items */ ++ if (res) { ++ size_t dst_name_len = 0; ++ unsigned int i; ++ size_t sz = 0; ++ ++ if (res->dst_name) { ++ dst_name_len = strlen(res->dst_name) + 1; ++ sz += KDBUS_ITEM_SIZE(dst_name_len); ++ } ++ ++ for (i = 0; i < res->data_count; ++i) { ++ struct kdbus_vec v; ++ struct kdbus_memfd m; ++ ++ switch (res->data[i].type) { ++ case KDBUS_MSG_DATA_VEC: ++ sz += KDBUS_ITEM_SIZE(sizeof(v)); ++ break; ++ ++ case KDBUS_MSG_DATA_MEMFD: ++ sz += KDBUS_ITEM_SIZE(sizeof(m)); ++ break; ++ } ++ } ++ ++ if (sz) { ++ struct kdbus_item *items, *item; ++ ++ items = kmalloc(sz, GFP_KERNEL); ++ if (!items) { ++ ret = -ENOMEM; ++ goto exit_free_entry; ++ } ++ ++ item = items; ++ ++ if (res->dst_name) ++ item = kdbus_item_set(item, KDBUS_ITEM_DST_NAME, ++ res->dst_name, ++ dst_name_len); ++ ++ for (i = 0; i < res->data_count; ++i) { ++ struct kdbus_msg_data *d = res->data + i; ++ struct kdbus_memfd m = {}; ++ struct kdbus_vec v = {}; ++ ++ switch (d->type) { ++ case KDBUS_MSG_DATA_VEC: ++ v.size = d->size; ++ v.offset = d->vec.off; ++ if (v.offset != ~0ULL) ++ v.offset += payload_off; ++ ++ item = kdbus_item_set(item, ++ KDBUS_ITEM_PAYLOAD_OFF, ++ &v, sizeof(v)); ++ break; ++ ++ case KDBUS_MSG_DATA_MEMFD: ++ /* ++ * Remember the location of memfds, so ++ * we can override the content from ++ * kdbus_queue_entry_install(). ++ */ ++ entry->memfd_offset[memfd_cnt++] = ++ msg_size + ++ (char *)item - (char *)items + ++ offsetof(struct kdbus_item, ++ memfd); ++ ++ item = kdbus_item_set(item, ++ KDBUS_ITEM_PAYLOAD_MEMFD, ++ &m, sizeof(m)); ++ break; ++ } ++ } ++ ++ kvec[0].iov_base = items; ++ kvec[0].iov_len = sz; ++ ++ ret = kdbus_pool_slice_copy_kvec(entry->slice, size, ++ kvec, 1, sz); ++ kfree(items); ++ ++ if (ret < 0) ++ goto exit_free_entry; ++ ++ size += sz; ++ } ++ ++ /* ++ * Remember the location of the FD part, so we can override the ++ * content in kdbus_queue_entry_install(). ++ */ ++ if (res->fds_count) { ++ entry->fds_offset = size; ++ size += KDBUS_ITEM_SIZE(sizeof(int) * res->fds_count); ++ } ++ } ++ ++ /* finally, copy over the actual message payload */ ++ if (kmsg->iov_count) { ++ ret = kdbus_pool_slice_copy_iovec(entry->slice, payload_off, ++ kmsg->iov, ++ kmsg->iov_count, ++ kmsg->pool_size); ++ if (ret < 0) ++ goto exit_free_entry; ++ } ++ ++ return entry; ++ ++exit_free_entry: ++ kdbus_queue_entry_free(entry); ++ return ERR_PTR(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) ++{ ++ if (!entry) ++ return; ++ ++ lockdep_assert_held(&entry->conn->lock); ++ ++ kdbus_queue_entry_unlink(entry); ++ kdbus_reply_unref(entry->reply); ++ ++ if (entry->slice) { ++ kdbus_conn_quota_dec(entry->conn, entry->user, ++ kdbus_pool_slice_size(entry->slice), ++ entry->msg_res ? ++ entry->msg_res->fds_count : 0); ++ kdbus_pool_slice_release(entry->slice); ++ kdbus_user_unref(entry->user); ++ } ++ ++ kdbus_msg_resources_unref(entry->msg_res); ++ kdbus_meta_conn_unref(entry->conn_meta); ++ kdbus_meta_proc_unref(entry->proc_meta); ++ kdbus_conn_unref(entry->conn); ++ kfree(entry->memfd_offset); ++ kfree(entry); ++} ++ ++/** ++ * kdbus_queue_entry_install() - install message components into the ++ * receiver's process ++ * @entry: The queue entry to install ++ * @return_flags: Pointer to store the return flags for userspace ++ * @install_fds: Whether or not to install associated file descriptors ++ * ++ * This function will create a slice to transport the message header, the ++ * metadata items and other items for information stored in @entry, and ++ * store it as entry->slice. ++ * ++ * If @install_fds is %true, file descriptors will as well be installed. ++ * This function must always be called from the task context of the receiver. ++ * ++ * Return: 0 on success. ++ */ ++int kdbus_queue_entry_install(struct kdbus_queue_entry *entry, ++ u64 *return_flags, bool install_fds) ++{ ++ u64 msg_size = entry->meta_offset; ++ struct kdbus_conn *conn_dst = entry->conn; ++ struct kdbus_msg_resources *res; ++ bool incomplete_fds = false; ++ struct kvec kvec[2]; ++ size_t memfds = 0; ++ int i, ret; ++ ++ lockdep_assert_held(&conn_dst->lock); ++ ++ if (entry->proc_meta || entry->conn_meta) { ++ size_t meta_size; ++ ++ ret = kdbus_meta_export(entry->proc_meta, ++ entry->conn_meta, ++ entry->attach_flags, ++ entry->slice, ++ entry->meta_offset, ++ &meta_size); ++ if (ret < 0) ++ return ret; ++ ++ msg_size += meta_size; ++ } ++ ++ /* Update message size at offset 0 */ ++ kvec[0].iov_base = &msg_size; ++ kvec[0].iov_len = sizeof(msg_size); ++ ++ ret = kdbus_pool_slice_copy_kvec(entry->slice, 0, kvec, 1, ++ sizeof(msg_size)); ++ if (ret < 0) ++ return ret; ++ ++ res = entry->msg_res; ++ ++ if (!res) ++ return 0; ++ ++ if (res->fds_count) { ++ struct kdbus_item_header hdr; ++ size_t off; ++ int *fds; ++ ++ fds = kmalloc_array(res->fds_count, sizeof(int), GFP_KERNEL); ++ if (!fds) ++ return -ENOMEM; ++ ++ for (i = 0; i < res->fds_count; i++) { ++ if (install_fds) { ++ fds[i] = get_unused_fd_flags(O_CLOEXEC); ++ if (fds[i] >= 0) ++ fd_install(fds[i], ++ get_file(res->fds[i])); ++ else ++ incomplete_fds = true; ++ } else { ++ fds[i] = -1; ++ } ++ } ++ ++ off = entry->fds_offset; ++ ++ hdr.type = KDBUS_ITEM_FDS; ++ hdr.size = KDBUS_ITEM_HEADER_SIZE + ++ sizeof(int) * res->fds_count; ++ ++ kvec[0].iov_base = &hdr; ++ kvec[0].iov_len = sizeof(hdr); ++ ++ kvec[1].iov_base = fds; ++ kvec[1].iov_len = sizeof(int) * res->fds_count; ++ ++ ret = kdbus_pool_slice_copy_kvec(entry->slice, off, ++ kvec, 2, hdr.size); ++ kfree(fds); ++ ++ if (ret < 0) ++ return ret; ++ } ++ ++ for (i = 0; i < res->data_count; ++i) { ++ struct kdbus_msg_data *d = res->data + i; ++ struct kdbus_memfd m; ++ ++ if (d->type != KDBUS_MSG_DATA_MEMFD) ++ continue; ++ ++ m.start = d->memfd.start; ++ m.size = d->size; ++ m.fd = -1; ++ ++ if (install_fds) { ++ m.fd = get_unused_fd_flags(O_CLOEXEC); ++ if (m.fd < 0) { ++ m.fd = -1; ++ incomplete_fds = true; ++ } else { ++ fd_install(m.fd, ++ get_file(d->memfd.file)); ++ } ++ } ++ ++ kvec[0].iov_base = &m; ++ kvec[0].iov_len = sizeof(m); ++ ++ ret = kdbus_pool_slice_copy_kvec(entry->slice, ++ entry->memfd_offset[memfds++], ++ kvec, 1, sizeof(m)); ++ if (ret < 0) ++ return ret; ++ } ++ ++ if (incomplete_fds) ++ *return_flags |= KDBUS_RECV_RETURN_INCOMPLETE_FDS; ++ ++ return 0; ++} ++ ++/** ++ * kdbus_queue_entry_enqueue() - enqueue an entry ++ * @entry: entry to enqueue ++ * @reply: reply to link to this entry (or NULL if none) ++ * ++ * This enqueues an unqueued entry into the message queue of the linked ++ * connection. It also binds a reply object to the entry so we can remember it ++ * when the message is moved. ++ * ++ * Once this call returns (and the connection lock is released), this entry can ++ * be dequeued by the target connection. Note that the entry will not be removed ++ * from the queue until it is destroyed. ++ */ ++void kdbus_queue_entry_enqueue(struct kdbus_queue_entry *entry, ++ struct kdbus_reply *reply) ++{ ++ lockdep_assert_held(&entry->conn->lock); ++ ++ if (WARN_ON(entry->reply) || WARN_ON(!list_empty(&entry->entry))) ++ return; ++ ++ entry->reply = kdbus_reply_ref(reply); ++ kdbus_queue_entry_link(entry); ++} ++ ++/** ++ * kdbus_queue_entry_move() - move queue entry ++ * @e: queue entry to move ++ * @dst: destination connection to queue the entry on ++ * ++ * This moves a queue entry onto a different connection. It allocates a new ++ * slice on the target connection and copies the message over. If the copy ++ * succeeded, we move the entry from @src to @dst. ++ * ++ * On failure, the entry is left untouched. ++ * ++ * The queue entry must be queued right now, and after the call succeeds it will ++ * be queued on the destination, but no longer on the source. ++ * ++ * The caller must hold the connection lock of the source *and* destination. ++ * ++ * Return: 0 on success, negative error code on failure. ++ */ ++int kdbus_queue_entry_move(struct kdbus_queue_entry *e, ++ struct kdbus_conn *dst) ++{ ++ struct kdbus_pool_slice *slice = NULL; ++ struct kdbus_conn *src = e->conn; ++ size_t size, fds; ++ int ret; ++ ++ lockdep_assert_held(&src->lock); ++ lockdep_assert_held(&dst->lock); ++ ++ if (WARN_ON(IS_ERR(e->user)) || WARN_ON(list_empty(&e->entry))) ++ return -EINVAL; ++ if (src == dst) ++ return 0; ++ ++ size = kdbus_pool_slice_size(e->slice); ++ fds = e->msg_res ? e->msg_res->fds_count : 0; ++ ++ ret = kdbus_conn_quota_inc(dst, e->user, size, fds); ++ if (ret < 0) ++ return ret; ++ ++ slice = kdbus_pool_slice_alloc(dst->pool, size, true); ++ if (IS_ERR(slice)) { ++ ret = PTR_ERR(slice); ++ slice = NULL; ++ goto error; ++ } ++ ++ ret = kdbus_pool_slice_copy(slice, e->slice); ++ if (ret < 0) ++ goto error; ++ ++ kdbus_queue_entry_unlink(e); ++ kdbus_conn_quota_dec(src, e->user, size, fds); ++ kdbus_pool_slice_release(e->slice); ++ kdbus_conn_unref(e->conn); ++ ++ e->slice = slice; ++ e->conn = kdbus_conn_ref(dst); ++ kdbus_queue_entry_link(e); ++ ++ return 0; ++ ++error: ++ kdbus_pool_slice_release(slice); ++ kdbus_conn_quota_dec(dst, e->user, size, fds); ++ return ret; ++} +diff --git a/ipc/kdbus/queue.h b/ipc/kdbus/queue.h +new file mode 100644 +index 000000000000..7f2db96fe308 +--- /dev/null ++++ b/ipc/kdbus/queue.h +@@ -0,0 +1,92 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> ++ * ++ * 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_user; ++ ++/** ++ * struct kdbus_queue - a connection's message queue ++ * @msg_list: List head for kdbus_queue_entry objects ++ * @msg_prio_queue: RB tree root for messages, sorted by priority ++ * @msg_prio_highest: Link to the RB node referencing the message with the ++ * highest priority in the tree. ++ */ ++struct kdbus_queue { ++ 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 ++ * @slice: Slice in the receiver's pool for the message ++ * @attach_flags: Attach flags used during slice allocation ++ * @meta_offset: Offset of first metadata item in slice ++ * @fds_offset: Offset of FD item in slice ++ * @memfd_offset: Array of slice-offsets for all memfd items ++ * @priority: Message priority ++ * @dst_name_id: The sequence number of the name this message is ++ * addressed to, 0 for messages sent to an ID ++ * @msg_res: Message resources ++ * @proc_meta: Process metadata, captured at message arrival ++ * @conn_meta: Connection metadata, captured at message arrival ++ * @reply: The reply block if a reply to this message is expected ++ * @user: User used for accounting ++ */ ++struct kdbus_queue_entry { ++ struct list_head entry; ++ struct rb_node prio_node; ++ struct list_head prio_entry; ++ ++ struct kdbus_pool_slice *slice; ++ ++ u64 attach_flags; ++ size_t meta_offset; ++ size_t fds_offset; ++ size_t *memfd_offset; ++ ++ s64 priority; ++ u64 dst_name_id; ++ ++ struct kdbus_msg_resources *msg_res; ++ struct kdbus_meta_proc *proc_meta; ++ struct kdbus_meta_conn *conn_meta; ++ struct kdbus_reply *reply; ++ struct kdbus_conn *conn; ++ struct kdbus_user *user; ++}; ++ ++struct kdbus_kmsg; ++ ++void kdbus_queue_init(struct kdbus_queue *queue); ++struct kdbus_queue_entry *kdbus_queue_peek(struct kdbus_queue *queue, ++ s64 priority, bool use_priority); ++ ++struct kdbus_queue_entry *kdbus_queue_entry_new(struct kdbus_conn *conn_dst, ++ const struct kdbus_kmsg *kmsg, ++ struct kdbus_user *user); ++void kdbus_queue_entry_free(struct kdbus_queue_entry *entry); ++int kdbus_queue_entry_install(struct kdbus_queue_entry *entry, ++ u64 *return_flags, bool install_fds); ++void kdbus_queue_entry_enqueue(struct kdbus_queue_entry *entry, ++ struct kdbus_reply *reply); ++int kdbus_queue_entry_move(struct kdbus_queue_entry *entry, ++ struct kdbus_conn *dst); ++ ++#endif /* __KDBUS_QUEUE_H */ +diff --git a/ipc/kdbus/reply.c b/ipc/kdbus/reply.c +new file mode 100644 +index 000000000000..6b3bd81bbb4d +--- /dev/null ++++ b/ipc/kdbus/reply.c +@@ -0,0 +1,259 @@ ++#include <linux/init.h> ++#include <linux/mm.h> ++#include <linux/module.h> ++#include <linux/mutex.h> ++#include <linux/slab.h> ++#include <linux/uio.h> ++ ++#include "bus.h" ++#include "connection.h" ++#include "endpoint.h" ++#include "message.h" ++#include "metadata.h" ++#include "names.h" ++#include "domain.h" ++#include "item.h" ++#include "notify.h" ++#include "policy.h" ++#include "reply.h" ++#include "util.h" ++ ++/** ++ * kdbus_reply_new() - Allocate and set up a new kdbus_reply object ++ * @reply_src: The connection a reply is expected from ++ * @reply_dst: The connection this reply object belongs to ++ * @msg: Message associated with the reply ++ * @name_entry: Name entry used to send the message ++ * @sync: Whether or not to make this reply synchronous ++ * ++ * Allocate and fill a new kdbus_reply object. ++ * ++ * Return: New kdbus_conn object on success, ERR_PTR on error. ++ */ ++struct kdbus_reply *kdbus_reply_new(struct kdbus_conn *reply_src, ++ struct kdbus_conn *reply_dst, ++ const struct kdbus_msg *msg, ++ struct kdbus_name_entry *name_entry, ++ bool sync) ++{ ++ struct kdbus_reply *r; ++ int ret = 0; ++ ++ if (atomic_inc_return(&reply_dst->request_count) > ++ KDBUS_CONN_MAX_REQUESTS_PENDING) { ++ ret = -EMLINK; ++ goto exit_dec_request_count; ++ } ++ ++ r = kzalloc(sizeof(*r), GFP_KERNEL); ++ if (!r) { ++ ret = -ENOMEM; ++ goto exit_dec_request_count; ++ } ++ ++ kref_init(&r->kref); ++ INIT_LIST_HEAD(&r->entry); ++ r->reply_src = kdbus_conn_ref(reply_src); ++ 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; ++ } ++ ++exit_dec_request_count: ++ if (ret < 0) { ++ atomic_dec(&reply_dst->request_count); ++ return ERR_PTR(ret); ++ } ++ ++ return r; ++} ++ ++static void __kdbus_reply_free(struct kref *kref) ++{ ++ struct kdbus_reply *reply = ++ container_of(kref, struct kdbus_reply, kref); ++ ++ atomic_dec(&reply->reply_dst->request_count); ++ kdbus_conn_unref(reply->reply_src); ++ kdbus_conn_unref(reply->reply_dst); ++ kfree(reply); ++} ++ ++/** ++ * kdbus_reply_ref() - Increase reference on kdbus_reply ++ * @r: The reply, may be %NULL ++ * ++ * Return: The reply object with an extra reference ++ */ ++struct kdbus_reply *kdbus_reply_ref(struct kdbus_reply *r) ++{ ++ if (r) ++ kref_get(&r->kref); ++ return r; ++} ++ ++/** ++ * kdbus_reply_unref() - Decrease reference on kdbus_reply ++ * @r: The reply, may be %NULL ++ * ++ * Return: NULL ++ */ ++struct kdbus_reply *kdbus_reply_unref(struct kdbus_reply *r) ++{ ++ if (r) ++ kref_put(&r->kref, __kdbus_reply_free); ++ return NULL; ++} ++ ++/** ++ * kdbus_reply_link() - Link reply object into target connection ++ * @r: Reply to link ++ */ ++void kdbus_reply_link(struct kdbus_reply *r) ++{ ++ if (WARN_ON(!list_empty(&r->entry))) ++ return; ++ ++ list_add(&r->entry, &r->reply_dst->reply_list); ++ kdbus_reply_ref(r); ++} ++ ++/** ++ * kdbus_reply_unlink() - Unlink reply object from target connection ++ * @r: Reply to unlink ++ */ ++void kdbus_reply_unlink(struct kdbus_reply *r) ++{ ++ if (!list_empty(&r->entry)) { ++ list_del_init(&r->entry); ++ kdbus_reply_unref(r); ++ } ++} ++ ++/** ++ * kdbus_sync_reply_wakeup() - Wake a synchronously blocking reply ++ * @reply: The reply object ++ * @err: Error code to set on the remote side ++ * ++ * Remove the synchronous reply object from its connection reply_list, and ++ * wake up remote peer (method origin) with the appropriate synchronous reply ++ * code. ++ */ ++void kdbus_sync_reply_wakeup(struct kdbus_reply *reply, int err) ++{ ++ if (WARN_ON(!reply->sync)) ++ return; ++ ++ reply->waiting = false; ++ reply->err = err; ++ wake_up_interruptible(&reply->reply_dst->wait); ++} ++ ++/** ++ * kdbus_reply_find() - Find the corresponding reply object ++ * @replying: The replying connection or NULL ++ * @reply_dst: The connection the reply will be sent to ++ * (method origin) ++ * @cookie: The cookie of the requesting message ++ * ++ * Lookup a reply object that should be sent as a reply by ++ * @replying to @reply_dst with the given cookie. ++ * ++ * Callers must take the @reply_dst lock. ++ * ++ * Return: the corresponding reply object or NULL if not found ++ */ ++struct kdbus_reply *kdbus_reply_find(struct kdbus_conn *replying, ++ struct kdbus_conn *reply_dst, ++ u64 cookie) ++{ ++ struct kdbus_reply *r, *reply = NULL; ++ ++ list_for_each_entry(r, &reply_dst->reply_list, entry) { ++ if (r->cookie == cookie && ++ (!replying || r->reply_src == replying)) { ++ reply = r; ++ break; ++ } ++ } ++ ++ return reply; ++} ++ ++/** ++ * kdbus_reply_list_scan_work() - Worker callback to scan the replies of a ++ * connection for exceeded timeouts ++ * @work: Work struct of the connection to scan ++ * ++ * Walk the list of replies stored with a connection and look for entries ++ * that have exceeded their timeout. If such an entry is found, a timeout ++ * notification is sent to the waiting peer, and the reply is removed from ++ * the list. ++ * ++ * The work is rescheduled to the nearest timeout found during the list ++ * iteration. ++ */ ++void kdbus_reply_list_scan_work(struct work_struct *work) ++{ ++ struct kdbus_conn *conn = ++ container_of(work, struct kdbus_conn, work.work); ++ struct kdbus_reply *reply, *reply_tmp; ++ u64 deadline = ~0ULL; ++ struct timespec64 ts; ++ u64 now; ++ ++ 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; ++ ++ WARN_ON(reply->reply_dst != conn); ++ ++ 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->ep->bus, conn->id, ++ reply->cookie); ++ ++ kdbus_reply_unlink(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->ep->bus); ++} +diff --git a/ipc/kdbus/reply.h b/ipc/kdbus/reply.h +new file mode 100644 +index 000000000000..68d52321a917 +--- /dev/null ++++ b/ipc/kdbus/reply.h +@@ -0,0 +1,68 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> ++ * ++ * 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_REPLY_H ++#define __KDBUS_REPLY_H ++ ++/** ++ * struct kdbus_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_src: The connection the reply will be sent from ++ * @reply_dst: The connection the reply will be sent to ++ * @queue_entry: The queue entry 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_reply { ++ struct kref kref; ++ struct list_head entry; ++ struct kdbus_conn *reply_src; ++ 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; ++}; ++ ++struct kdbus_reply *kdbus_reply_new(struct kdbus_conn *reply_src, ++ struct kdbus_conn *reply_dst, ++ const struct kdbus_msg *msg, ++ struct kdbus_name_entry *name_entry, ++ bool sync); ++ ++struct kdbus_reply *kdbus_reply_ref(struct kdbus_reply *r); ++struct kdbus_reply *kdbus_reply_unref(struct kdbus_reply *r); ++ ++void kdbus_reply_link(struct kdbus_reply *r); ++void kdbus_reply_unlink(struct kdbus_reply *r); ++ ++struct kdbus_reply *kdbus_reply_find(struct kdbus_conn *replying, ++ struct kdbus_conn *reply_dst, ++ u64 cookie); ++ ++void kdbus_sync_reply_wakeup(struct kdbus_reply *reply, int err); ++void kdbus_reply_list_scan_work(struct work_struct *work); ++ ++#endif /* __KDBUS_REPLY_H */ +diff --git a/ipc/kdbus/util.h b/ipc/kdbus/util.h +index 9caadb337912..740b19880985 100644 +--- a/ipc/kdbus/util.h ++++ b/ipc/kdbus/util.h +@@ -18,7 +18,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/kdbus-add-documentation.patch b/kdbus-add-documentation.patch new file mode 100644 index 000000000..7aef55a01 --- /dev/null +++ b/kdbus-add-documentation.patch @@ -0,0 +1,7488 @@ +From: Daniel Mack <daniel@zonque.org> +Date: Thu, 11 Sep 2014 21:50:47 +0200 +Subject: [PATCH] kdbus: add documentation + +kdbus is a system for low-latency, low-overhead, easy to use +interprocess communication (IPC). + +The interface to all functions in this driver is implemented via ioctls +on files exposed through a filesystem called 'kdbusfs'. The default +mount point of kdbusfs is /sys/fs/kdbus. This patch adds detailed +documentation about the kernel level API design. + +This patch adds a set of comprehensive set of DocBook files which +can be turned into man-pages using 'make mandocs', or into HTML +files with 'make htmldocs'. + +Signed-off-by: Daniel Mack <daniel@zonque.org> +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Djalal Harouni <tixxdz@opendz.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + Documentation/Makefile | 2 +- + Documentation/kdbus/Makefile | 30 + + Documentation/kdbus/kdbus.bus.xml | 360 +++++++++ + Documentation/kdbus/kdbus.connection.xml | 1252 +++++++++++++++++++++++++++++ + Documentation/kdbus/kdbus.endpoint.xml | 436 ++++++++++ + Documentation/kdbus/kdbus.fs.xml | 124 +++ + Documentation/kdbus/kdbus.item.xml | 840 ++++++++++++++++++++ + Documentation/kdbus/kdbus.match.xml | 553 +++++++++++++ + Documentation/kdbus/kdbus.message.xml | 1277 ++++++++++++++++++++++++++++++ + Documentation/kdbus/kdbus.name.xml | 711 +++++++++++++++++ + Documentation/kdbus/kdbus.policy.xml | 406 ++++++++++ + Documentation/kdbus/kdbus.pool.xml | 320 ++++++++ + Documentation/kdbus/kdbus.xml | 1012 +++++++++++++++++++++++ + Documentation/kdbus/stylesheet.xsl | 16 + + Makefile | 1 + + 15 files changed, 7339 insertions(+), 1 deletion(-) + create mode 100644 Documentation/kdbus/Makefile + create mode 100644 Documentation/kdbus/kdbus.bus.xml + create mode 100644 Documentation/kdbus/kdbus.connection.xml + create mode 100644 Documentation/kdbus/kdbus.endpoint.xml + create mode 100644 Documentation/kdbus/kdbus.fs.xml + create mode 100644 Documentation/kdbus/kdbus.item.xml + create mode 100644 Documentation/kdbus/kdbus.match.xml + create mode 100644 Documentation/kdbus/kdbus.message.xml + create mode 100644 Documentation/kdbus/kdbus.name.xml + create mode 100644 Documentation/kdbus/kdbus.policy.xml + create mode 100644 Documentation/kdbus/kdbus.pool.xml + create mode 100644 Documentation/kdbus/kdbus.xml + create mode 100644 Documentation/kdbus/stylesheet.xsl + +diff --git a/Documentation/Makefile b/Documentation/Makefile +index bc0548201755..e2127a76b5d6 100644 +--- a/Documentation/Makefile ++++ b/Documentation/Makefile +@@ -1,4 +1,4 @@ + subdir-y := accounting auxdisplay blackfin connector \ +- filesystems filesystems ia64 laptops mic misc-devices \ ++ filesystems filesystems ia64 kdbus laptops mic misc-devices \ + networking pcmcia prctl ptp spi timers vDSO video4linux \ + watchdog +diff --git a/Documentation/kdbus/Makefile b/Documentation/kdbus/Makefile +new file mode 100644 +index 000000000000..cd6b48ee41bf +--- /dev/null ++++ b/Documentation/kdbus/Makefile +@@ -0,0 +1,30 @@ ++DOCS := \ ++ kdbus.xml \ ++ kdbus.bus.xml \ ++ kdbus.connection.xml \ ++ kdbus.endpoint.xml \ ++ kdbus.fs.xml \ ++ kdbus.item.xml \ ++ kdbus.match.xml \ ++ kdbus.message.xml \ ++ kdbus.name.xml \ ++ kdbus.policy.xml \ ++ kdbus.pool.xml ++ ++XMLFILES := $(addprefix $(obj)/,$(DOCS)) ++MANFILES := $(patsubst %.xml, %.7, $(XMLFILES)) ++HTMLFILES := $(patsubst %.xml, %.html, $(XMLFILES)) ++ ++XMLTO_ARGS := -m $(obj)/stylesheet.xsl ++ ++%.7: %.xml ++ xmlto man $(XMLTO_ARGS) -o . $< ++ ++%.html: %.xml ++ xmlto html-nochunks $(XMLTO_ARGS) -o . $< ++ ++mandocs: $(MANFILES) ++ ++htmldocs: $(HTMLFILES) ++ ++clean-files := $(MANFILES) $(HTMLFILES) +diff --git a/Documentation/kdbus/kdbus.bus.xml b/Documentation/kdbus/kdbus.bus.xml +new file mode 100644 +index 000000000000..4d875e59ac02 +--- /dev/null ++++ b/Documentation/kdbus/kdbus.bus.xml +@@ -0,0 +1,360 @@ ++<?xml version='1.0'?> <!--*-nxml-*--> ++<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" ++ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> ++ ++<refentry id="kdbus.bus"> ++ ++ <refentryinfo> ++ <title>kdbus.bus</title> ++ <productname>kdbus.bus</productname> ++ </refentryinfo> ++ ++ <refmeta> ++ <refentrytitle>kdbus.bus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </refmeta> ++ ++ <refnamediv> ++ <refname>kdbus.bus</refname> ++ <refpurpose>kdbus bus</refpurpose> ++ </refnamediv> ++ ++ <refsect1> ++ <title>Description</title> ++ ++ <para> ++ A bus is a resource that is shared between connections in order to ++ transmit messages (see ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ ). ++ Each bus is independent, and operations on the bus will not have any ++ effect on other buses. A bus is a management entity that controls the ++ addresses of its connections, their policies and message transactions ++ performed via this bus. ++ </para> ++ <para> ++ Each bus is bound to the mount instance it was created on. It has a ++ custom name that is unique across all buses of a domain. In ++ <citerefentry> ++ <refentrytitle>kdbus.fs</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ , a bus is presented as a directory. No operations can be performed on ++ the bus itself; instead you need to perform the operations on an endpoint ++ associated with the bus. Endpoints are accessible as files underneath the ++ bus directory. A default endpoint called <constant>bus</constant> is ++ provided on each bus. ++ </para> ++ <para> ++ Bus names may be chosen freely except for one restriction: the name must ++ be prefixed with the numeric effective UID of the creator and a dash. This ++ is required to avoid namespace clashes between different users. When ++ creating a bus, the name that is passed in must be properly formatted, or ++ the kernel will refuse creation of the bus. Example: ++ <literal>1047-foobar</literal> is an acceptable name for a bus ++ registered by a user with UID 1047. However, ++ <literal>1024-foobar</literal> is not, and neither is ++ <literal>foobar</literal>. The UID must be provided in the ++ user-namespace of the bus owner. ++ </para> ++ <para> ++ To create a new bus, you need to open the control file of a domain and ++ employ the <constant>KDBUS_CMD_BUS_MAKE</constant> ioctl. The control ++ file descriptor that was used to issue ++ <constant>KDBUS_CMD_BUS_MAKE</constant> must not previously have been ++ used for any other control-ioctl and must be kept open for the entire ++ life-time of the created bus. Closing it will immediately cleanup the ++ entire bus and all its associated resources and endpoints. Every control ++ file descriptor can only be used to create a single new bus; from that ++ point on, it is not used for any further communication until the final ++ <citerefentry> ++ <refentrytitle>close</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry> ++ . ++ </para> ++ <para> ++ Each bus will generate a random, 128-bit UUID upon creation. This UUID ++ will be returned to creators of connections through ++ <varname>kdbus_cmd_hello.id128</varname> and can be used to uniquely ++ identify buses, even across different machines or containers. The UUID ++ will have its variant bits set to <literal>DCE</literal>, and denote ++ version 4 (random). For more details on UUIDs, see <ulink ++ url="https://en.wikipedia.org/wiki/Universally_unique_identifier"> ++ the Wikipedia article on UUIDs</ulink>. ++ </para> ++ ++ </refsect1> ++ ++ <refsect1> ++ <title>Creating buses</title> ++ <para> ++ To create a new bus, the <constant>KDBUS_CMD_BUS_MAKE</constant> ++ command is used. It takes a <type>struct kdbus_cmd</type> argument. ++ </para> ++ <programlisting> ++struct kdbus_cmd { ++ __u64 size; ++ __u64 flags; ++ __u64 return_flags; ++ struct kdbus_item items[0]; ++}; ++ </programlisting> ++ ++ <para>The fields in this struct are described below.</para> ++ ++ <variablelist> ++ <varlistentry> ++ <term><varname>size</varname></term> ++ <listitem><para> ++ The overall size of the struct, including its items. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>flags</varname></term> ++ <listitem><para>The flags for creation.</para> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_MAKE_ACCESS_GROUP</constant></term> ++ <listitem> ++ <para>Make the bus file group-accessible.</para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_MAKE_ACCESS_WORLD</constant></term> ++ <listitem> ++ <para>Make the bus file world-accessible.</para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_FLAG_NEGOTIATE</constant></term> ++ <listitem> ++ <para> ++ Requests a set of valid flags for this ioctl. When this bit is ++ set, no action is taken; the ioctl will return ++ <errorcode>0</errorcode>, and the <varname>flags</varname> ++ field will have all bits set that are valid for this command. ++ The <constant>KDBUS_FLAG_NEGOTIATE</constant> bit will be ++ cleared by the operation. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>return_flags</varname></term> ++ <listitem><para> ++ Flags returned by the kernel. Currently unused and always set to ++ <constant>0</constant> by the kernel. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>items</varname></term> ++ <listitem> ++ <para> ++ The following items (see ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ ) are expected for <constant>KDBUS_CMD_BUS_MAKE</constant>. ++ </para> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_MAKE_NAME</constant></term> ++ <listitem> ++ <para> ++ Contains a null-terminated string that identifies the ++ bus. The name must be unique across the kdbus domain and ++ must start with the effective UID of the caller, followed by ++ a '<literal>-</literal>' (dash). This item is mandatory. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_BLOOM_PARAMETER</constant></term> ++ <listitem> ++ <para> ++ Bus-wide bloom parameters passed in a ++ <type>struct kdbus_bloom_parameter</type>. These settings are ++ copied back to new connections verbatim. This item is ++ mandatory. See ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for a more detailed description of this item. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_ATTACH_FLAGS_RECV</constant></term> ++ <listitem> ++ <para> ++ An optional item that contains a set of required attach flags ++ that connections must allow. This item is used as a ++ negotiation measure during connection creation. If connections ++ do not satisfy the bus requirements, they are not allowed on ++ the bus. If not set, the bus does not require any metadata to ++ be attached; in this case connections are free to set their ++ own attach flags. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_ATTACH_FLAGS_SEND</constant></term> ++ <listitem> ++ <para> ++ An optional item that contains a set of attach flags that are ++ returned to connections when they query the bus creator ++ metadata. If not set, no metadata is returned. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_NEGOTIATE</constant></term> ++ <listitem><para> ++ With this item, programs can <emphasis>probe</emphasis> the ++ kernel for known item types. See ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more details. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ ++ <para> ++ Unrecognized items are rejected, and the ioctl will fail with ++ <varname>errno</varname> set to <constant>EINVAL</constant>. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Return value</title> ++ <para> ++ On success, all mentioned ioctl commands return <errorcode>0</errorcode>; ++ on error, <errorcode>-1</errorcode> is returned, and ++ <varname>errno</varname> is set to indicate the error. ++ If the issued ioctl is illegal for the file descriptor used, ++ <varname>errno</varname> will be set to <constant>ENOTTY</constant>. ++ </para> ++ ++ <refsect2> ++ <title> ++ <constant>KDBUS_CMD_BUS_MAKE</constant> may fail with the following ++ errors ++ </title> ++ ++ <variablelist> ++ <varlistentry> ++ <term><constant>EBADMSG</constant></term> ++ <listitem><para> ++ A mandatory item is missing. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EINVAL</constant></term> ++ <listitem><para> ++ The flags supplied in the <constant>struct kdbus_cmd</constant> ++ are invalid or the supplied name does not start with the current ++ UID and a '<literal>-</literal>' (dash). ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EEXIST</constant></term> ++ <listitem><para> ++ A bus of that name already exists. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>ESHUTDOWN</constant></term> ++ <listitem><para> ++ The kdbus mount instance for the bus was already shut down. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EMFILE</constant></term> ++ <listitem><para> ++ The maximum number of buses for the current user is exhausted. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect2> ++ </refsect1> ++ ++ <refsect1> ++ <title>See Also</title> ++ <simplelist type="inline"> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.endpoint</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.fs</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.name</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.pool</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ </simplelist> ++ </refsect1> ++</refentry> +diff --git a/Documentation/kdbus/kdbus.connection.xml b/Documentation/kdbus/kdbus.connection.xml +new file mode 100644 +index 000000000000..09852125b2d4 +--- /dev/null ++++ b/Documentation/kdbus/kdbus.connection.xml +@@ -0,0 +1,1252 @@ ++<?xml version='1.0'?> <!--*-nxml-*--> ++<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" ++ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> ++ ++<refentry id="kdbus.connection"> ++ ++ <refentryinfo> ++ <title>kdbus.connection</title> ++ <productname>kdbus.connection</productname> ++ </refentryinfo> ++ ++ <refmeta> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </refmeta> ++ ++ <refnamediv> ++ <refname>kdbus.connection</refname> ++ <refpurpose>kdbus connection</refpurpose> ++ </refnamediv> ++ ++ <refsect1> ++ <title>Description</title> ++ ++ <para> ++ Connections are identified by their <emphasis>connection ID</emphasis>, ++ internally implemented as a <type>uint64_t</type> counter. ++ The IDs of every newly created bus start at <constant>1</constant>, and ++ every new connection will increment the counter by <constant>1</constant>. ++ The IDs are not reused. ++ </para> ++ <para> ++ In higher level tools, the user visible representation of a connection is ++ defined by the D-Bus protocol specification as ++ <constant>":1.<ID>"</constant>. ++ </para> ++ <para> ++ Messages with a specific <type>uint64_t</type> destination ID are ++ directly delivered to the connection with the corresponding ID. Signal ++ messages (see ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>) ++ may be addressed to the special destination ID ++ <constant>KDBUS_DST_ID_BROADCAST</constant> (~0ULL) and will then ++ potentially be delivered to all currently active connections on the bus. ++ However, in order to receive any signal messages, clients must subscribe ++ to them by installing a match (see ++ <citerefentry> ++ <refentrytitle>kdbus.match</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ ). ++ </para> ++ <para> ++ Messages synthesized and sent directly by the kernel will carry the ++ special source ID <constant>KDBUS_SRC_ID_KERNEL</constant> (0). ++ </para> ++ <para> ++ In addition to the unique <type>uint64_t</type> connection ID, ++ established connections can request the ownership of ++ <emphasis>well-known names</emphasis>, 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 ++ <citerefentry> ++ <refentrytitle>kdbus.name</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ on name acquisition, the name registry, and the validity of names. ++ </para> ++ <para> ++ Messages can specify the special destination ID ++ <constant>KDBUS_DST_ID_NAME</constant> (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. ++ </para> ++ ++ <programlisting><![CDATA[ ++ +-------------------------------------------------------------------------+ ++ | +---------------+ +---------------------------+ | ++ | | 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 | | | | ++ | | | | | | ++ | | | | | | ++ | | | <-----------------------------------+ | | ++ | | | | | ++ | | | <----------------------------------------------+ | ++ | +---------------+ | ++ +-------------------------------------------------------------------------+ ++ ]]></programlisting> ++ </refsect1> ++ ++ <refsect1> ++ <title>Privileged connections</title> ++ <para> ++ A connection is considered <emphasis>privileged</emphasis> if the user ++ it was created by is the same that created the bus, or if the creating ++ task had <constant>CAP_IPC_OWNER</constant> set when it called ++ <constant>KDBUS_CMD_HELLO</constant> (see below). ++ </para> ++ <para> ++ Privileged connections have permission to employ certain restricted ++ functions and commands, which are explained below and in other kdbus ++ man-pages. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Activator and policy holder connection</title> ++ <para> ++ An <emphasis>activator</emphasis> connection is a placeholder for a ++ <emphasis>well-known name</emphasis>. Messages sent to such a connection ++ can be used to start an implementer connection, which will then get all ++ the messages from the activator copied over. An activator connection ++ cannot be used to send any message. ++ </para> ++ <para> ++ A <emphasis>policy holder</emphasis> 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. ++ </para> ++ <para> ++ The creation of activator or policy holder connections is restricted to ++ privileged users on the bus (see above). ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Monitor connections</title> ++ <para> ++ Monitors are eavesdropping connections that receive all the traffic on the ++ bus, but is invisible to other connections. Such connections have all ++ properties of any other, regular connection, except for the following ++ details: ++ </para> ++ ++ <itemizedlist> ++ <listitem><para> ++ They will get every message sent over the bus, both unicasts and ++ broadcasts. ++ </para></listitem> ++ ++ <listitem><para> ++ Installing matches for signal messages is neither necessary ++ nor allowed. ++ </para></listitem> ++ ++ <listitem><para> ++ They cannot send messages or be directly addressed as receiver. ++ </para></listitem> ++ ++ <listitem><para> ++ They cannot own well-known names. Therefore, they also can't operate as ++ activators. ++ </para></listitem> ++ ++ <listitem><para> ++ Their creation and destruction will not cause ++ <constant>KDBUS_ITEM_ID_{ADD,REMOVE}</constant> (see ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>). ++ </para></listitem> ++ ++ <listitem><para> ++ They are not listed with their unique name in name registry dumps ++ (see <constant>KDBUS_CMD_NAME_LIST</constant> in ++ <citerefentry> ++ <refentrytitle>kdbus.name</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>), so other connections cannot detect the presence of ++ a monitor. ++ </para></listitem> ++ </itemizedlist> ++ <para> ++ The creation of monitor connections is restricted to privileged users on ++ the bus (see above). ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Creating connections</title> ++ <para> ++ A connection to a bus is created by opening an endpoint file (see ++ <citerefentry> ++ <refentrytitle>kdbus.endpoint</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>) ++ of a bus and becoming an active client with the ++ <constant>KDBUS_CMD_HELLO</constant> ioctl. Every 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. ++ </para> ++ <para> ++ The <constant>KDBUS_CMD_HELLO</constant> ioctl takes a <type>struct ++ kdbus_cmd_hello</type> as argument. ++ </para> ++ ++ <programlisting> ++struct kdbus_cmd_hello { ++ __u64 size; ++ __u64 flags; ++ __u64 return_flags; ++ __u64 attach_flags_send; ++ __u64 attach_flags_recv; ++ __u64 bus_flags; ++ __u64 id; ++ __u64 pool_size; ++ __u64 offset; ++ __u8 id128[16]; ++ struct kdbus_item items[0]; ++}; ++ </programlisting> ++ ++ <para>The fields in this struct are described below.</para> ++ ++ <variablelist> ++ <varlistentry> ++ <term><varname>size</varname></term> ++ <listitem><para> ++ The overall size of the struct, including its items. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>flags</varname></term> ++ <listitem> ++ <para>Flags to apply to this connection</para> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_HELLO_ACCEPT_FD</constant></term> ++ <listitem> ++ <para> ++ When this flag is set, the connection can be sent file ++ descriptors as message payload of unicast messages. If it's ++ not set, an attempt to send file descriptors will result in ++ <constant>-ECOMM</constant> on the sender's side. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_HELLO_ACTIVATOR</constant></term> ++ <listitem> ++ <para> ++ Make this connection an activator (see above). With this bit ++ set, an item of type <constant>KDBUS_ITEM_NAME</constant> has ++ to be attached. This item describes the well-known name this ++ connection should be an activator for. ++ A connection can not be an activator and a policy holder at ++ the same time time, so this bit is not allowed together with ++ <constant>KDBUS_HELLO_POLICY_HOLDER</constant>. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_HELLO_POLICY_HOLDER</constant></term> ++ <listitem> ++ <para> ++ Make this connection a policy holder (see above). With this ++ bit set, an item of type <constant>KDBUS_ITEM_NAME</constant> ++ has to be attached. This item describes the well-known name ++ this connection should hold a policy for. ++ A connection can not be an activator and a policy holder at ++ the same time time, so this bit is not allowed together with ++ <constant>KDBUS_HELLO_ACTIVATOR</constant>. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_HELLO_MONITOR</constant></term> ++ <listitem> ++ <para> ++ Make this connection a monitor connection (see above). ++ </para> ++ <para> ++ This flag can only be set by privileged bus connections. See ++ below for more information. ++ A connection can not be monitor and an activator or a policy ++ holder at the same time time, so this bit is not allowed ++ together with <constant>KDBUS_HELLO_ACTIVATOR</constant> or ++ <constant>KDBUS_HELLO_POLICY_HOLDER</constant>. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_FLAG_NEGOTIATE</constant></term> ++ <listitem> ++ <para> ++ Requests a set of valid flags for this ioctl. When this bit is ++ set, no action is taken; the ioctl will return ++ <errorcode>0</errorcode>, and the <varname>flags</varname> ++ field will have all bits set that are valid for this command. ++ The <constant>KDBUS_FLAG_NEGOTIATE</constant> bit will be ++ cleared by the operation. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>return_flags</varname></term> ++ <listitem><para> ++ Flags returned by the kernel. Currently unused and always set to ++ <constant>0</constant> by the kernel. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>attach_flags_send</varname></term> ++ <listitem><para> ++ Set the bits for metadata this connection permits to be sent to the ++ receiving peer. Only metadata items that are both allowed to be sent ++ by the sender and that are requested by the receiver will be attached ++ to the message. Note, however, that the bus may optionally require ++ some of those bits to be set. If the match fails, the ioctl will fail ++ with <varname>errno</varname> set to ++ <constant>ECONNREFUSED</constant>. In either case, when returning the ++ field will be set to the mask of metadata items that are enforced by ++ the bus with the <constant>KDBUS_FLAGS_KERNEL</constant> bit set as ++ well. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>attach_flags_recv</varname></term> ++ <listitem><para> ++ Request the attachment of metadata for each message received by this ++ connection. See ++ <citerefentry> ++ <refentrytitle>kdbus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for information about metadata, and ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ regarding items in general. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>bus_flags</varname></term> ++ <listitem><para> ++ Upon successful completion of the ioctl, this member will contain the ++ flags of the bus it connected to. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>id</varname></term> ++ <listitem><para> ++ Upon successful completion of the command, this member will contain ++ the numerical ID of the new connection. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>pool_size</varname></term> ++ <listitem><para> ++ The size of the communication pool, in bytes. The pool can be ++ accessed by calling ++ <citerefentry> ++ <refentrytitle>mmap</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry> ++ on the file descriptor that was used to issue the ++ <constant>KDBUS_CMD_HELLO</constant> ioctl. ++ The pool size of a connection must be greater than ++ <constant>0</constant> and a multiple of ++ <constant>PAGE_SIZE</constant>. See ++ <citerefentry> ++ <refentrytitle>kdbus.pool</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>offset</varname></term> ++ <listitem><para> ++ The kernel will return the offset in the pool where returned details ++ will be stored. See below. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>id128</varname></term> ++ <listitem><para> ++ Upon successful completion of the ioctl, this member will contain the ++ <emphasis>128-bit UUID</emphasis> of the connected bus. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>items</varname></term> ++ <listitem> ++ <para> ++ Variable list of items containing optional additional information. ++ The following items are currently expected/valid: ++ </para> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_CONN_DESCRIPTION</constant></term> ++ <listitem> ++ <para> ++ Contains a string that describes this connection, so it can ++ be identified later. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_NAME</constant></term> ++ <term><constant>KDBUS_ITEM_POLICY_ACCESS</constant></term> ++ <listitem> ++ <para> ++ For activators and policy holders only, combinations of ++ these two items describe policy access entries. See ++ <citerefentry> ++ <refentrytitle>kdbus.policy</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for further details. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_CREDS</constant></term> ++ <term><constant>KDBUS_ITEM_PIDS</constant></term> ++ <term><constant>KDBUS_ITEM_SECLABEL</constant></term> ++ <listitem> ++ <para> ++ Privileged bus users may submit these types in order to ++ create connections with faked credentials. This information ++ will be returned when peer information is queried by ++ <constant>KDBUS_CMD_CONN_INFO</constant>. See below for more ++ information on retrieving information on connections. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_NEGOTIATE</constant></term> ++ <listitem><para> ++ With this item, programs can <emphasis>probe</emphasis> the ++ kernel for known item types. See ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more details. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ ++ <para> ++ Unrecognized items are rejected, and the ioctl will fail with ++ <varname>errno</varname> set to <constant>EINVAL</constant>. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ ++ <para> ++ At the offset returned in the <varname>offset</varname> field of ++ <type>struct kdbus_cmd_hello</type>, the kernel will store items ++ of the following types: ++ </para> ++ ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_BLOOM_PARAMETER</constant></term> ++ <listitem> ++ <para> ++ Bloom filter parameter as defined by the bus creator. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ ++ <para> ++ The offset in the pool has to be freed with the ++ <constant>KDBUS_CMD_FREE</constant> ioctl. See ++ <citerefentry> ++ <refentrytitle>kdbus.pool</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for further information. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Retrieving information on a connection</title> ++ <para> ++ The <constant>KDBUS_CMD_CONN_INFO</constant> ioctl can be used to ++ retrieve credentials and properties of the initial creator of a ++ connection. This ioctl uses the following struct. ++ </para> ++ ++ <programlisting> ++struct kdbus_cmd_info { ++ __u64 size; ++ __u64 flags; ++ __u64 return_flags; ++ __u64 id; ++ __u64 attach_flags; ++ __u64 offset; ++ __u64 info_size; ++ struct kdbus_item items[0]; ++}; ++ </programlisting> ++ ++ <variablelist> ++ <varlistentry> ++ <term><varname>size</varname></term> ++ <listitem><para> ++ The overall size of the struct, including its items. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>flags</varname></term> ++ <listitem><para> ++ Currently, no flags are supported. ++ <constant>KDBUS_FLAG_NEGOTIATE</constant> is accepted to probe for ++ valid flags. If set, the ioctl will return <errorcode>0</errorcode>, ++ and the <varname>flags</varname> field is set to ++ <constant>0</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>return_flags</varname></term> ++ <listitem><para> ++ Flags returned by the kernel. Currently unused and always set to ++ <constant>0</constant> by the kernel. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>id</varname></term> ++ <listitem><para> ++ The numerical ID of the connection for which information is to be ++ retrieved. If set to a non-zero value, the ++ <constant>KDBUS_ITEM_OWNED_NAME</constant> item is ignored. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>flags</varname></term> ++ <listitem><para> ++ Specifies which metadata items should be attached to the answer. See ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>offset</varname></term> ++ <listitem><para> ++ When the ioctl returns, this field will contain the offset of the ++ connection information inside the caller's pool. See ++ <citerefentry> ++ <refentrytitle>kdbus.pool</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for further information. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>info_size</varname></term> ++ <listitem><para> ++ The kernel will return the size of the returned information, so ++ applications can optionally ++ <citerefentry> ++ <refentrytitle>mmap</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry> ++ specific parts of the pool. See ++ <citerefentry> ++ <refentrytitle>kdbus.pool</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for further information. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>items</varname></term> ++ <listitem> ++ <para> ++ The following items are expected for ++ <constant>KDBUS_CMD_CONN_INFO</constant>. ++ </para> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_OWNED_NAME</constant></term> ++ <listitem> ++ <para> ++ Contains the well-known name of the connection to look up as. ++ This item is mandatory if the <varname>id</varname> field is ++ set to 0. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_NEGOTIATE</constant></term> ++ <listitem><para> ++ With this item, programs can <emphasis>probe</emphasis> the ++ kernel for known item types. See ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more details. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ <para> ++ Unrecognized items are rejected, and the ioctl will fail with ++ <varname>errno</varname> set to <constant>EINVAL</constant>. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ ++ <para> ++ When the ioctl returns, the following struct will be stored in the ++ caller's pool at <varname>offset</varname>. The fields in this struct ++ are described below. ++ </para> ++ ++ <programlisting> ++struct kdbus_info { ++ __u64 size; ++ __u64 id; ++ __u64 flags; ++ struct kdbus_item items[0]; ++}; ++ </programlisting> ++ ++ <variablelist> ++ <varlistentry> ++ <term><varname>size</varname></term> ++ <listitem><para> ++ The overall size of the struct, including its items. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>id</varname></term> ++ <listitem><para> ++ The connection's unique ID. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>flags</varname></term> ++ <listitem><para> ++ The connection's flags as specified when it was created. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>items</varname></term> ++ <listitem> ++ <para> ++ Depending on the <varname>flags</varname> field in ++ <type>struct kdbus_cmd_info</type>, items of types ++ <constant>KDBUS_ITEM_OWNED_NAME</constant> and ++ <constant>KDBUS_ITEM_CONN_DESCRIPTION</constant> may follow here. ++ <constant>KDBUS_ITEM_NEGOTIATE</constant> is also allowed. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ ++ <para> ++ Once the caller is finished with parsing the return buffer, it needs to ++ employ the <constant>KDBUS_CMD_FREE</constant> command for the offset, in ++ order to free the buffer part. See ++ <citerefentry> ++ <refentrytitle>kdbus.pool</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for further information. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Getting information about a connection's bus creator</title> ++ <para> ++ The <constant>KDBUS_CMD_BUS_CREATOR_INFO</constant> ioctl takes the same ++ struct as <constant>KDBUS_CMD_CONN_INFO</constant>, 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. ++ </para> ++ <para> ++ In response to this call, a slice in the connection's pool is allocated ++ and filled with an object of type <type>struct kdbus_info</type>, ++ pointed to by the ioctl's <varname>offset</varname> field. ++ </para> ++ ++ <programlisting> ++struct kdbus_info { ++ __u64 size; ++ __u64 id; ++ __u64 flags; ++ struct kdbus_item items[0]; ++}; ++ </programlisting> ++ ++ <variablelist> ++ <varlistentry> ++ <term><varname>size</varname></term> ++ <listitem><para> ++ The overall size of the struct, including its items. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>id</varname></term> ++ <listitem><para> ++ The bus ID. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>flags</varname></term> ++ <listitem><para> ++ The bus flags as specified when it was created. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>items</varname></term> ++ <listitem> ++ <para> ++ Metadata information is stored in items here. The item list ++ contains a <constant>KDBUS_ITEM_MAKE_NAME</constant> item that ++ indicates the bus name of the calling connection. ++ <constant>KDBUS_ITEM_NEGOTIATE</constant> is allowed to probe ++ for known item types. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ ++ <para> ++ Once the caller is finished with parsing the return buffer, it needs to ++ employ the <constant>KDBUS_CMD_FREE</constant> command for the offset, in ++ order to free the buffer part. See ++ <citerefentry> ++ <refentrytitle>kdbus.pool</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for further information. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Updating connection details</title> ++ <para> ++ Some of a connection's details can be updated with the ++ <constant>KDBUS_CMD_CONN_UPDATE</constant> ioctl, using the file ++ descriptor that was used to create the connection. The update command ++ uses the following struct. ++ </para> ++ ++ <programlisting> ++struct kdbus_cmd { ++ __u64 size; ++ __u64 flags; ++ __u64 return_flags; ++ struct kdbus_item items[0]; ++}; ++ </programlisting> ++ ++ <variablelist> ++ <varlistentry> ++ <term><varname>size</varname></term> ++ <listitem><para> ++ The overall size of the struct, including its items. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>flags</varname></term> ++ <listitem><para> ++ Currently, no flags are supported. ++ <constant>KDBUS_FLAG_NEGOTIATE</constant> is accepted to probe for ++ valid flags. If set, the ioctl will return <errorcode>0</errorcode>, ++ and the <varname>flags</varname> field is set to ++ <constant>0</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>return_flags</varname></term> ++ <listitem><para> ++ Flags returned by the kernel. Currently unused and always set to ++ <constant>0</constant> by the kernel. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>items</varname></term> ++ <listitem> ++ <para> ++ Items to describe the connection details to be updated. The ++ following item types are supported. ++ </para> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_ATTACH_FLAGS_SEND</constant></term> ++ <listitem> ++ <para> ++ Supply a new set of metadata items that this connection ++ permits to be sent along with messages. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_ATTACH_FLAGS_RECV</constant></term> ++ <listitem> ++ <para> ++ Supply a new set of metadata items that this connection ++ requests to be attached to each message. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_NAME</constant></term> ++ <term><constant>KDBUS_ITEM_POLICY_ACCESS</constant></term> ++ <listitem> ++ <para> ++ Policy holder connections may supply a new set of policy ++ information with these items. For other connection types, ++ <constant>EOPNOTSUPP</constant> is returned in ++ <varname>errno</varname>. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_NEGOTIATE</constant></term> ++ <listitem><para> ++ With this item, programs can <emphasis>probe</emphasis> the ++ kernel for known item types. See ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more details. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ ++ <para> ++ Unrecognized items are rejected, and the ioctl will fail with ++ <varname>errno</varname> set to <constant>EINVAL</constant>. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect1> ++ ++ <refsect1> ++ <title>Termination of connections</title> ++ <para> ++ A connection can be terminated by simply calling ++ <citerefentry> ++ <refentrytitle>close</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry> ++ on its file descriptor. All pending incoming messages will be discarded, ++ and the memory allocated by the pool will be freed. ++ </para> ++ ++ <para> ++ An alternative way of closing down a connection is via the ++ <constant>KDBUS_CMD_BYEBYE</constant> ioctl. This ioctl will succeed only ++ if the message queue of the connection is empty at the time of closing; ++ otherwise, the ioctl will fail with <varname>errno</varname> set to ++ <constant>EBUSY</constant>. 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. The ioctl takes an argument of ++ type <type>struct kdbus_cmd</type>. ++ </para> ++ ++ <programlisting> ++struct kdbus_cmd { ++ __u64 size; ++ __u64 flags; ++ __u64 return_flags; ++ struct kdbus_item items[0]; ++}; ++ </programlisting> ++ ++ <variablelist> ++ <varlistentry> ++ <term><varname>size</varname></term> ++ <listitem><para> ++ The overall size of the struct, including its items. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>flags</varname></term> ++ <listitem><para> ++ Currently, no flags are supported. ++ <constant>KDBUS_FLAG_NEGOTIATE</constant> is accepted to probe for ++ valid flags. If set, the ioctl will fail with ++ <varname>errno</varname> set to <constant>EPROTO</constant>, and ++ the <varname>flags</varname> field is set to <constant>0</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>return_flags</varname></term> ++ <listitem><para> ++ Flags returned by the kernel. Currently unused and always set to ++ <constant>0</constant> by the kernel. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>items</varname></term> ++ <listitem> ++ <para> ++ Items to describe the connection details to be updated. The ++ following item types are supported. ++ </para> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_NEGOTIATE</constant></term> ++ <listitem><para> ++ With this item, programs can <emphasis>probe</emphasis> the ++ kernel for known item types. See ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more details. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ ++ <para> ++ Unrecognized items are rejected, and the ioctl will fail with ++ <varname>errno</varname> set to <constant>EINVAL</constant>. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect1> ++ ++ <refsect1> ++ <title>Return value</title> ++ <para> ++ On success, all mentioned ioctl commands return <errorcode>0</errorcode>; ++ on error, <errorcode>-1</errorcode> is returned, and ++ <varname>errno</varname> is set to indicate the error. ++ If the issued ioctl is illegal for the file descriptor used, ++ <varname>errno</varname> will be set to <constant>ENOTTY</constant>. ++ </para> ++ ++ <refsect2> ++ <title> ++ <constant>KDBUS_CMD_HELLO</constant> may fail with the following ++ errors ++ </title> ++ ++ <variablelist> ++ <varlistentry> ++ <term><constant>EFAULT</constant></term> ++ <listitem><para> ++ The supplied pool size was 0 or not a multiple of the page size. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EINVAL</constant></term> ++ <listitem><para> ++ The flags supplied in <type>struct kdbus_cmd_hello</type> ++ are invalid. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EINVAL</constant></term> ++ <listitem><para> ++ An illegal combination of ++ <constant>KDBUS_HELLO_MONITOR</constant>, ++ <constant>KDBUS_HELLO_ACTIVATOR</constant> and ++ <constant>KDBUS_HELLO_POLICY_HOLDER</constant> was passed in ++ <varname>flags</varname>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EINVAL</constant></term> ++ <listitem><para> ++ An invalid set of items was supplied. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>ECONNREFUSED</constant></term> ++ <listitem><para> ++ The attach_flags_send field did not satisfy the requirements of ++ the bus. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EPERM</constant></term> ++ <listitem><para> ++ A <constant>KDBUS_ITEM_CREDS</constant> items was supplied, but the ++ current user is not privileged. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>ESHUTDOWN</constant></term> ++ <listitem><para> ++ The bus you were trying to connect to has already been shut down. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EMFILE</constant></term> ++ <listitem><para> ++ The maximum number of connections on the bus has been reached. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EOPNOTSUPP</constant></term> ++ <listitem><para> ++ The endpoint does not support the connection flags supplied in ++ <type>struct kdbus_cmd_hello</type>. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect2> ++ ++ <refsect2> ++ <title> ++ <constant>KDBUS_CMD_BYEBYE</constant> may fail with the following ++ errors ++ </title> ++ ++ <variablelist> ++ <varlistentry> ++ <term><constant>EALREADY</constant></term> ++ <listitem><para> ++ The connection has already been shut down. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EBUSY</constant></term> ++ <listitem><para> ++ There are still messages queued up in the connection's pool. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect2> ++ ++ <refsect2> ++ <title> ++ <constant>KDBUS_CMD_CONN_INFO</constant> may fail with the following ++ errors ++ </title> ++ ++ <variablelist> ++ <varlistentry> ++ <term><constant>EINVAL</constant></term> ++ <listitem><para> ++ Invalid flags, or neither an ID nor a name was provided, or the ++ name is invalid. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>ESRCH</constant></term> ++ <listitem><para> ++ Connection lookup by name failed. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>ENXIO</constant></term> ++ <listitem><para> ++ No connection with the provided connection ID found. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect2> ++ ++ <refsect2> ++ <title> ++ <constant>KDBUS_CMD_CONN_UPDATE</constant> may fail with the following ++ errors ++ </title> ++ ++ <variablelist> ++ <varlistentry> ++ <term><constant>EINVAL</constant></term> ++ <listitem><para> ++ Illegal flags or items. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EINVAL</constant></term> ++ <listitem><para> ++ Wildcards submitted in policy entries, or illegal sequence ++ of policy items. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EOPNOTSUPP</constant></term> ++ <listitem><para> ++ Operation not supported by connection. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>E2BIG</constant></term> ++ <listitem><para> ++ Too many policy items attached. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect2> ++ </refsect1> ++ ++ <refsect1> ++ <title>See Also</title> ++ <simplelist type="inline"> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.bus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.endpoint</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.name</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.policy</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.pool</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ </simplelist> ++ </refsect1> ++</refentry> +diff --git a/Documentation/kdbus/kdbus.endpoint.xml b/Documentation/kdbus/kdbus.endpoint.xml +new file mode 100644 +index 000000000000..76e325d4e931 +--- /dev/null ++++ b/Documentation/kdbus/kdbus.endpoint.xml +@@ -0,0 +1,436 @@ ++<?xml version='1.0'?> <!--*-nxml-*--> ++<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" ++ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> ++ ++<refentry id="kdbus.endpoint"> ++ ++ <refentryinfo> ++ <title>kdbus.endpoint</title> ++ <productname>kdbus.endpoint</productname> ++ </refentryinfo> ++ ++ <refmeta> ++ <refentrytitle>kdbus.endpoint</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </refmeta> ++ ++ <refnamediv> ++ <refname>kdbus.endpoint</refname> ++ <refpurpose>kdbus endpoint</refpurpose> ++ </refnamediv> ++ ++ <refsect1> ++ <title>Description</title> ++ ++ <para> ++ Endpoints are entry points to a bus (see ++ <citerefentry> ++ <refentrytitle>kdbus.bus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>). ++ 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). An endpoint is presented as file underneath the directory ++ of the parent bus. ++ </para> ++ <para> ++ To create a custom endpoint, open the default endpoint ++ (<literal>bus</literal>) and use the ++ <constant>KDBUS_CMD_ENDPOINT_MAKE</constant> ioctl with ++ <type>struct kdbus_cmd</type>. Custom endpoints always have a policy ++ database that, by default, forbids any operation. You have to explicitly ++ install policy entries to allow any operation on this endpoint. ++ </para> ++ <para> ++ Once <constant>KDBUS_CMD_ENDPOINT_MAKE</constant> succeeded, the new ++ endpoint will appear in the filesystem ++ (<citerefentry> ++ <refentrytitle>kdbus.bus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>), and the used file descriptor will manage the ++ newly created endpoint resource. It cannot be used to manage further ++ resources and must be kept open as long as the endpoint is needed. The ++ endpoint will be terminated as soon as the file descriptor is closed. ++ </para> ++ <para> ++ Endpoint names may be chosen freely except for one restriction: the name ++ must be prefixed with the numeric effective UID of the creator and a dash. ++ This is required to avoid namespace clashes between different users. When ++ creating an endpoint, the name that is passed in must be properly ++ formatted or the kernel will refuse creation of the endpoint. Example: ++ <literal>1047-my-endpoint</literal> is an acceptable name for an ++ endpoint registered by a user with UID 1047. However, ++ <literal>1024-my-endpoint</literal> is not, and neither is ++ <literal>my-endpoint</literal>. The UID must be provided in the ++ user-namespace of the bus. ++ </para> ++ <para> ++ To create connections to a bus, use <constant>KDBUS_CMD_HELLO</constant> ++ on a file descriptor returned by <function>open()</function> on an ++ endpoint node. See ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for further details. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Creating custom endpoints</title> ++ <para> ++ To create a new endpoint, the ++ <constant>KDBUS_CMD_ENDPOINT_MAKE</constant> command is used. Along with ++ the endpoint's name, which will be used to expose the endpoint in the ++ <citerefentry> ++ <refentrytitle>kdbus.fs</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>, ++ the command also optionally takes items to set up the endpoint's ++ <citerefentry> ++ <refentrytitle>kdbus.policy</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>. ++ <constant>KDBUS_CMD_ENDPOINT_MAKE</constant> takes a ++ <type>struct kdbus_cmd</type> argument. ++ </para> ++ <programlisting> ++struct kdbus_cmd { ++ __u64 size; ++ __u64 flags; ++ __u64 return_flags; ++ struct kdbus_item items[0]; ++}; ++ </programlisting> ++ ++ <para>The fields in this struct are described below.</para> ++ ++ <variablelist> ++ <varlistentry> ++ <term><varname>size</varname></term> ++ <listitem><para> ++ The overall size of the struct, including its items. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>flags</varname></term> ++ <listitem><para>The flags for creation.</para> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_MAKE_ACCESS_GROUP</constant></term> ++ <listitem> ++ <para>Make the endpoint file group-accessible.</para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_MAKE_ACCESS_WORLD</constant></term> ++ <listitem> ++ <para>Make the endpoint file world-accessible.</para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_FLAG_NEGOTIATE</constant></term> ++ <listitem> ++ <para> ++ Requests a set of valid flags for this ioctl. When this bit is ++ set, no action is taken; the ioctl will return ++ <errorcode>0</errorcode>, and the <varname>flags</varname> ++ field will have all bits set that are valid for this command. ++ The <constant>KDBUS_FLAG_NEGOTIATE</constant> bit will be ++ cleared by the operation. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>return_flags</varname></term> ++ <listitem><para> ++ Flags returned by the kernel. Currently unused and always set to ++ <constant>0</constant> by the kernel. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>items</varname></term> ++ <listitem> ++ <para> ++ The following items are expected for ++ <constant>KDBUS_CMD_ENDPOINT_MAKE</constant>. ++ </para> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_MAKE_NAME</constant></term> ++ <listitem> ++ <para>Contains a string to identify the endpoint name.</para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_NAME</constant></term> ++ <term><constant>KDBUS_ITEM_POLICY_ACCESS</constant></term> ++ <listitem> ++ <para> ++ These items are used to set the policy attached to the ++ endpoint. For more details on bus and endpoint policies, see ++ <citerefentry> ++ <refentrytitle>kdbus.policy</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ <para> ++ Unrecognized items are rejected, and the ioctl will fail with ++ <varname>errno</varname> set to <varname>EINVAL</varname>. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect1> ++ ++ <refsect1> ++ <title>Updating endpoints</title> ++ <para> ++ To update an existing endpoint, the ++ <constant>KDBUS_CMD_ENDPOINT_UPDATE</constant> command is used on the file ++ descriptor that was used to create the update, using ++ <constant>KDBUS_CMD_ENDPOINT_MAKE</constant>. The only relevant detail of ++ the endpoint that can be updated is the policy. When the command is ++ employed, the policy of the endpoint is <emphasis>replaced</emphasis> ++ atomically with the new set of rules. ++ The command takes a <type>struct kdbus_cmd</type> argument. ++ </para> ++ <programlisting> ++struct kdbus_cmd { ++ __u64 size; ++ __u64 flags; ++ __u64 return_flags; ++ struct kdbus_item items[0]; ++}; ++ </programlisting> ++ ++ <para>The fields in this struct are described below.</para> ++ ++ <variablelist> ++ <varlistentry> ++ <term><varname>size</varname></term> ++ <listitem><para> ++ The overall size of the struct, including its items. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>flags</varname></term> ++ <listitem><para> ++ Unused for this command. ++ <constant>KDBUS_FLAG_NEGOTIATE</constant> is accepted to probe for ++ valid flags. If set, the ioctl will return <errorcode>0</errorcode>, ++ and the <varname>flags</varname> field is set to ++ <constant>0</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>return_flags</varname></term> ++ <listitem><para> ++ Flags returned by the kernel. Currently unused and always set to ++ <constant>0</constant> by the kernel. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>items</varname></term> ++ <listitem> ++ <para> ++ The following items are expected for ++ <constant>KDBUS_CMD_ENDPOINT_UPDATE</constant>. ++ </para> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_NAME</constant></term> ++ <term><constant>KDBUS_ITEM_POLICY_ACCESS</constant></term> ++ <listitem> ++ <para> ++ These items are used to set the policy attached to the ++ endpoint. For more details on bus and endpoint policies, see ++ <citerefentry> ++ <refentrytitle>kdbus.policy</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>. ++ Existing policy is atomically replaced with the new rules ++ provided. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_NEGOTIATE</constant></term> ++ <listitem><para> ++ With this item, programs can <emphasis>probe</emphasis> the ++ kernel for known item types. See ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more details. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ <para> ++ Unrecognized items are rejected, and the ioctl will fail with ++ <varname>errno</varname> set to <constant>EINVAL</constant>. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect1> ++ ++ <refsect1> ++ <title>Return value</title> ++ <para> ++ On success, all mentioned ioctl commands return <errorcode>0</errorcode>; ++ on error, <errorcode>-1</errorcode> is returned, and ++ <varname>errno</varname> is set to indicate the error. ++ If the issued ioctl is illegal for the file descriptor used, ++ <varname>errno</varname> will be set to <constant>ENOTTY</constant>. ++ </para> ++ ++ <refsect2> ++ <title> ++ <constant>KDBUS_CMD_ENDPOINT_MAKE</constant> may fail with the ++ following errors ++ </title> ++ ++ <variablelist> ++ <varlistentry> ++ <term><constant>EINVAL</constant></term> ++ <listitem><para> ++ The flags supplied in the <type>struct kdbus_cmd</type> ++ are invalid. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EINVAL</constant></term> ++ <listitem><para> ++ Illegal combination of <constant>KDBUS_ITEM_NAME</constant> and ++ <constant>KDBUS_ITEM_POLICY_ACCESS</constant> was provided. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EEXIST</constant></term> ++ <listitem><para> ++ An endpoint of that name already exists. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EPERM</constant></term> ++ <listitem><para> ++ The calling user is not privileged. See ++ <citerefentry> ++ <refentrytitle>kdbus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for information about privileged users. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect2> ++ ++ <refsect2> ++ <title> ++ <constant>KDBUS_CMD_ENDPOINT_UPDATE</constant> may fail with the ++ following errors ++ </title> ++ ++ <variablelist> ++ <varlistentry> ++ <term><constant>EINVAL</constant></term> ++ <listitem><para> ++ The flags supplied in <type>struct kdbus_cmd</type> ++ are invalid. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EINVAL</constant></term> ++ <listitem><para> ++ Illegal combination of <constant>KDBUS_ITEM_NAME</constant> and ++ <constant>KDBUS_ITEM_POLICY_ACCESS</constant> was provided. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EEXIST</constant></term> ++ <listitem><para> ++ An endpoint of that name already exists. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect2> ++ </refsect1> ++ ++ <refsect1> ++ <title>See Also</title> ++ <simplelist type="inline"> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.bus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.endpoint</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.fs</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.name</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.pool</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ </simplelist> ++ </refsect1> ++</refentry> +diff --git a/Documentation/kdbus/kdbus.fs.xml b/Documentation/kdbus/kdbus.fs.xml +new file mode 100644 +index 000000000000..8c2a90e10b66 +--- /dev/null ++++ b/Documentation/kdbus/kdbus.fs.xml +@@ -0,0 +1,124 @@ ++<?xml version='1.0'?> <!--*-nxml-*--> ++<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" ++ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> ++ ++<refentry id="kdbus_fs"> ++ ++ <refentryinfo> ++ <title>kdbus.fs</title> ++ <productname>kdbus.fs</productname> ++ </refentryinfo> ++ ++ <refmeta> ++ <refentrytitle>kdbus.fs</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </refmeta> ++ ++ <refnamediv> ++ <refname>kdbus.fs</refname> ++ <refpurpose>kdbus file system</refpurpose> ++ </refnamediv> ++ ++ <refsect1> ++ <title>File-system Layout</title> ++ ++ <para> ++ The <emphasis>kdbusfs</emphasis> pseudo filesystem provides access to ++ kdbus entities, such as <emphasis>buses</emphasis> and ++ <emphasis>endpoints</emphasis>. Each time the filesystem is mounted, ++ a new, isolated kdbus instance is created, which is independent from the ++ other instances. ++ </para> ++ <para> ++ The system-wide standard mount point for <emphasis>kdbusfs</emphasis> is ++ <constant>/sys/fs/kdbus</constant>. ++ </para> ++ ++ <para> ++ Buses are represented as directories in the file system layout, whereas ++ endpoints are exposed as files inside these directories. At the top-level, ++ a <emphasis>control</emphasis> node is present, which can be opened to ++ create new buses via the <constant>KDBUS_CMD_BUS_MAKE</constant> ioctl. ++ Each <emphasis>bus</emphasis> shows a default endpoint called ++ <varname>bus</varname>, which can be opened to either create a connection ++ with the <constant>KDBUS_CMD_HELLO</constant> ioctl, or to create new ++ custom endpoints for the bus with ++ <constant>KDBUS_CMD_ENDPOINT_MAKE</constant>. See ++ <citerefentry> ++ <refentrytitle>kdbus.bus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>, ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> and ++ <citerefentry> ++ <refentrytitle>kdbus.endpoint</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more details. ++ </para> ++ ++ <para>Following, you can see an example layout of the ++ <emphasis>kdbusfs</emphasis> filesystem:</para> ++ ++<programlisting> ++ /sys/fs/kdbus/ ; mount-point ++ |-- 0-system ; bus directory ++ | |-- bus ; default endpoint ++ | `-- 1017-custom ; custom endpoint ++ |-- 1000-user ; bus directory ++ | |-- bus ; default endpoint ++ | |-- 1000-service-A ; custom endpoint ++ | `-- 1000-service-B ; custom endpoint ++ `-- control ; control file ++</programlisting> ++ </refsect1> ++ ++ <refsect1> ++ <title>Mounting instances</title> ++ <para> ++ In order to get a new and separate kdbus environment, a new instance ++ of <emphasis>kdbusfs</emphasis> can be mounted like this: ++ </para> ++<programlisting> ++ # mount -t kdbusfs kdbusfs /tmp/new_kdbus/ ++</programlisting> ++ </refsect1> ++ ++ <refsect1> ++ <title>See Also</title> ++ <simplelist type="inline"> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.bus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.endpoint</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>mount</refentrytitle> ++ <manvolnum>8</manvolnum> ++ </citerefentry> ++ </member> ++ </simplelist> ++ </refsect1> ++</refentry> +diff --git a/Documentation/kdbus/kdbus.item.xml b/Documentation/kdbus/kdbus.item.xml +new file mode 100644 +index 000000000000..bfe47362097f +--- /dev/null ++++ b/Documentation/kdbus/kdbus.item.xml +@@ -0,0 +1,840 @@ ++<?xml version='1.0'?> <!--*-nxml-*--> ++<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" ++ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> ++ ++<refentry id="kdbus"> ++ ++ <refentryinfo> ++ <title>kdbus.item</title> ++ <productname>kdbus item</productname> ++ </refentryinfo> ++ ++ <refmeta> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </refmeta> ++ ++ <refnamediv> ++ <refname>kdbus.item</refname> ++ <refpurpose>kdbus item structure, layout and usage</refpurpose> ++ </refnamediv> ++ ++ <refsect1> ++ <title>Description</title> ++ ++ <para> ++ To flexibly augment transport structures, data blobs of type ++ <type>struct kdbus_item</type> can be attached to the structs passed ++ into the ioctls. Some ioctls make items of certain types mandatory, ++ others are optional. Items that are unsupported by ioctls they are ++ attached to will cause the ioctl to fail with <varname>errno</varname> ++ set to <constant>EINVAL</constant>. ++ Items are also used for information stored in a connection's ++ <emphasis>pool</emphasis>, such as received messages, name lists or ++ requested connection or bus owner information. Depending on the type of ++ an item, its total size is either fixed or variable. ++ </para> ++ ++ <refsect2> ++ <title>Chaining items</title> ++ <para> ++ Whenever items are used as part of the kdbus kernel API, they are ++ embedded in structs that are embedded inside structs that themselves ++ include a size field containing the overall size of the structure. ++ This allows multiple items to be chained up, and an item iterator ++ (see below) is capable of detecting the end of an item chain. ++ </para> ++ </refsect2> ++ ++ <refsect2> ++ <title>Alignment</title> ++ <para> ++ The kernel expects all items to be aligned to 8-byte boundaries. ++ Unaligned items will cause the ioctl they are used with to fail ++ with <varname>errno</varname> set to <constant>EINVAL</constant>. ++ An item that has an unaligned size itself hence needs to be padded ++ if it is followed by another item. ++ </para> ++ </refsect2> ++ ++ <refsect2> ++ <title>Iterating items</title> ++ <para> ++ A simple iterator would iterate over the items until the items have ++ reached the embedding structure's overall size. An example ++ implementation is shown below. ++ </para> ++ ++ <programlisting><![CDATA[ ++#define KDBUS_ALIGN8(val) (((val) + 7) & ~7) ++ ++#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) && \ ++ ((uint8_t *)(item) >= (uint8_t *)(head)); \ ++ item = KDBUS_ITEM_NEXT(item)) ++ ]]></programlisting> ++ </refsect2> ++ </refsect1> ++ ++ <refsect1> ++ <title>Item layout</title> ++ <para> ++ A <type>struct kdbus_item</type> consists of a ++ <varname>size</varname> field, describing its overall size, and a ++ <varname>type</varname> field, both 64 bit wide. They are followed by ++ a union to store information that is specific to the item's type. ++ The struct layout is shown below. ++ </para> ++ ++ <programlisting> ++struct kdbus_item { ++ __u64 size; ++ __u64 type; ++ /* item payload - see below */ ++ 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_pids pids; ++ 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; ++ }; ++}; ++ </programlisting> ++ ++ <para> ++ <type>struct kdbus_item</type> should never be used to allocate ++ an item instance, as its size may grow in future releases of the API. ++ Instead, it should be manually assembled by storing the ++ <varname>size</varname>, <varname>type</varname> and payload to a ++ struct of its own. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Item types</title> ++ ++ <refsect2> ++ <title>Negotiation item</title> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_NEGOTIATE</constant></term> ++ <listitem><para> ++ With this item is attached to any ioctl, programs can ++ <emphasis>probe</emphasis> the kernel for known item items. ++ The item carries an array of <type>uint64_t</type> values in ++ <varname>item.data64</varname>, each set to an item type to ++ probe. The kernel will reset each member of this array that is ++ not recognized as valid item type to <constant>0</constant>. ++ This way, users can negotiate kernel features at start-up to ++ keep newer userspace compatible with older kernels. This item ++ is never attached by the kernel in response to any command. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect2> ++ ++ <refsect2> ++ <title>Command specific items</title> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_PAYLOAD_VEC</constant></term> ++ <term><constant>KDBUS_ITEM_PAYLOAD_OFF</constant></term> ++ <listitem><para> ++ Messages are directly copied by the sending process into the ++ receiver's ++ <citerefentry> ++ <refentrytitle>kdbus.pool</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>. ++ This 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. <constant>KDBUS_ITEM_PAYLOAD_VEC</constant> ++ is used when <emphasis>sending</emphasis> message. The item ++ references a memory address when the payload data can be found. ++ <constant>KDBUS_ITEM_PAYLOAD_OFF</constant> is used when messages ++ are <emphasis>received</emphasis>, and the ++ <constant>offset</constant> value describes the offset inside the ++ receiving connection's ++ <citerefentry> ++ <refentrytitle>kdbus.pool</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ where the message payload can be found. See ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information on passing of payload data along with a ++ message. ++ <programlisting> ++struct kdbus_vec { ++ __u64 size; ++ union { ++ __u64 address; ++ __u64 offset; ++ }; ++}; ++ </programlisting> ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_PAYLOAD_MEMFD</constant></term> ++ <listitem><para> ++ Transports a file descriptor of a <emphasis>memfd</emphasis> in ++ <type>struct kdbus_memfd</type> in <varname>item.memfd</varname>. ++ The <varname>size</varname> field has to match the actual size of ++ the memfd that was specified when it was created. The ++ <varname>start</varname> parameter denotes the offset inside the ++ memfd at which the referenced payload starts. See ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information on passing of payload data along with a ++ message. ++ <programlisting> ++struct kdbus_memfd { ++ __u64 start; ++ __u64 size; ++ int fd; ++ __u32 __pad; ++}; ++ </programlisting> ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_FDS</constant></term> ++ <listitem><para> ++ Contains an array of <emphasis>file descriptors</emphasis>. ++ When used with <constant>KDBUS_CMD_SEND</constant>, the values of ++ this array must be filled with valid file descriptor numbers. ++ When received as item attached to a message, the array will ++ contain the numbers of the installed file descriptors, or ++ <constant>-1</constant> in case an error occurred. ++ file descriptor. ++ In either case, the number of entries in the array is derived from ++ the item's total size. See ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect2> ++ ++ <refsect2> ++ <title>Items specific to some commands</title> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_CANCEL_FD</constant></term> ++ <listitem><para> ++ Transports a file descriptor that can be used to cancel a ++ synchronous <constant>KDBUS_CMD_SEND</constant> operation by ++ writing to it. The file descriptor is stored in ++ <varname>item.fd[0]</varname>. The item may only contain one ++ file descriptor. See ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information on this item and how to use it. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_BLOOM_PARAMETER</constant></term> ++ <listitem><para> ++ Contains a set of <emphasis>bloom parameters</emphasis> as ++ <type>struct kdbus_bloom_parameter</type> in ++ <varname>item.bloom_parameter</varname>. ++ The item is passed from userspace to kernel during the ++ <constant>KDBUS_CMD_BUS_MAKE</constant> ioctl, and returned ++ verbatim when <constant>KDBUS_CMD_HELLO</constant> is called. ++ The kernel does not use the bloom parameters, but they need to ++ be known by each connection on the bus in order to define the ++ bloom filter hash details. See ++ <citerefentry> ++ <refentrytitle>kdbus.match</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information on matching and bloom filters. ++ <programlisting> ++struct kdbus_bloom_parameter { ++ __u64 size; ++ __u64 n_hash; ++}; ++ </programlisting> ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_BLOOM_FILTER</constant></term> ++ <listitem><para> ++ Carries a <emphasis>bloom filter</emphasis> as ++ <type>struct kdbus_bloom_filter</type> in ++ <varname>item.bloom_filter</varname>. It is mandatory to send this ++ item attached to a <type>struct kdbus_msg</type>, in case the ++ message is a signal. This item is never transported from kernel to ++ userspace. See ++ <citerefentry> ++ <refentrytitle>kdbus.match</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information on matching and bloom filters. ++ <programlisting> ++struct kdbus_bloom_filter { ++ __u64 generation; ++ __u64 data[0]; ++}; ++ </programlisting> ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_BLOOM_MASK</constant></term> ++ <listitem><para> ++ Transports a <emphasis>bloom mask</emphasis> as binary data blob ++ stored in <varname>item.data</varname>. This item is used to ++ describe a match into a connection's match database. See ++ <citerefentry> ++ <refentrytitle>kdbus.match</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information on matching and bloom filters. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_DST_NAME</constant></term> ++ <listitem><para> ++ Contains a <emphasis>well-known name</emphasis> to send a ++ message to, as null-terminated string in ++ <varname>item.str</varname>. This item is used with ++ <constant>KDBUS_CMD_SEND</constant>. See ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information on how to send a message. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_MAKE_NAME</constant></term> ++ <listitem><para> ++ Contains a <emphasis>bus name</emphasis> or ++ <emphasis>endpoint name</emphasis>, stored as null-terminated ++ string in <varname>item.str</varname>. This item is sent from ++ userspace to kernel when buses or endpoints are created, and ++ returned back to userspace when the bus creator information is ++ queried. See ++ <citerefentry> ++ <refentrytitle>kdbus.bus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ and ++ <citerefentry> ++ <refentrytitle>kdbus.endpoint</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_ATTACH_FLAGS_SEND</constant></term> ++ <term><constant>KDBUS_ITEM_ATTACH_FLAGS_RECV</constant></term> ++ <listitem><para> ++ Contains a set of <emphasis>attach flags</emphasis> at ++ <emphasis>send</emphasis> or <emphasis>receive</emphasis> time. See ++ <citerefentry> ++ <refentrytitle>kdbus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>, ++ <citerefentry> ++ <refentrytitle>kdbus.bus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> and ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information on attach flags. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_ID</constant></term> ++ <listitem><para> ++ Transports a connection's <emphasis>numerical ID</emphasis> of ++ a connection as <type>uint64_t</type> value in ++ <varname>item.id</varname>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_NAME</constant></term> ++ <listitem><para> ++ Transports a name associated with the ++ <emphasis>name registry</emphasis> as null-terminated string as ++ <type>struct kdbus_name</type> in ++ <varname>item.name</varname>. The <varname>flags</varname> ++ contains the flags of the name. See ++ <citerefentry> ++ <refentrytitle>kdbus.name</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information on how to access the name registry of a bus. ++ <programlisting> ++struct kdbus_name { ++ __u64 flags; ++ char name[0]; ++}; ++ </programlisting> ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect2> ++ ++ <refsect2> ++ <title>Items attached by the kernel as metadata</title> ++ ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_TIMESTAMP</constant></term> ++ <listitem><para> ++ Contains both the <emphasis>monotonic</emphasis> and the ++ <emphasis>realtime</emphasis> timestamp, taken when the message ++ was processed on the kernel side. ++ Stored as <type>struct kdbus_timestamp</type> in ++ <varname>item.timestamp</varname>. ++ <programlisting> ++struct kdbus_timestamp { ++ __u64 seqnum; ++ __u64 monotonic_ns; ++ __u64 realtime_ns; ++}; ++ </programlisting> ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_CREDS</constant></term> ++ <listitem><para> ++ Contains a set of <emphasis>user</emphasis> and ++ <emphasis>group</emphasis> information as 32-bit values, in the ++ usual four flavors: real, effective, saved and filesystem related. ++ Stored as <type>struct kdbus_creds</type> in ++ <varname>item.creds</varname>. ++ <programlisting> ++struct kdbus_creds { ++ __u32 uid; ++ __u32 euid; ++ __u32 suid; ++ __u32 fsuid; ++ __u32 gid; ++ __u32 egid; ++ __u32 sgid; ++ __u32 fsgid; ++}; ++ </programlisting> ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_PIDS</constant></term> ++ <listitem><para> ++ Contains the <emphasis>PID</emphasis>, <emphasis>TID</emphasis> ++ and <emphasis>parent PID (PPID)</emphasis> of a remote peer. ++ Stored as <type>struct kdbus_pids</type> in ++ <varname>item.pids</varname>. ++ <programlisting> ++struct kdbus_pids { ++ __u64 pid; ++ __u64 tid; ++ __u64 ppid; ++}; ++ </programlisting> ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_AUXGROUPS</constant></term> ++ <listitem><para> ++ Contains the <emphasis>auxiliary (supplementary) groups</emphasis> ++ a remote peer is a member of, stored as array of ++ <type>uint32_t</type> values in <varname>item.data32</varname>. ++ The array length can be determined by looking at the item's total ++ size, subtracting the size of the header and and dividing the ++ remainder by <constant>sizeof(uint32_t)</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_OWNED_NAME</constant></term> ++ <listitem><para> ++ Contains a <emphasis>well-known name</emphasis> currently owned ++ by a connection. The name is stored as null-terminated string in ++ <varname>item.str</varname>. Its length can also be derived from ++ the item's total size. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_TID_COMM</constant> [*]</term> ++ <listitem><para> ++ Contains the <emphasis>comm</emphasis> string of a task's ++ <emphasis>TID</emphasis> (thread ID), stored as null-terminated ++ string in <varname>item.str</varname>. Its length can also be ++ derived from the item's total size. Receivers of this item should ++ not use its contents for any kind of security measures. See below. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_PID_COMM</constant> [*]</term> ++ <listitem><para> ++ Contains the <emphasis>comm</emphasis> string of a task's ++ <emphasis>PID</emphasis> (process ID), stored as null-terminated ++ string in <varname>item.str</varname>. Its length can also be ++ derived from the item's total size. Receivers of this item should ++ not use its contents for any kind of security measures. See below. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_EXE</constant> [*]</term> ++ <listitem><para> ++ Contains the <emphasis>path to the executable</emphasis> of a task, ++ stored as null-terminated string in <varname>item.str</varname>. Its ++ length can also be derived from the item's total size. Receivers of ++ this item should not use its contents for any kind of security ++ measures. See below. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_CMDLINE</constant> [*]</term> ++ <listitem><para> ++ Contains the <emphasis>command line arguments</emphasis> of a ++ task, stored as an <emphasis>array</emphasis> of null-terminated ++ strings in <varname>item.str</varname>. The total length of all ++ strings in the array can be derived from the item's total size. ++ Receivers of this item should not use its contents for any kind ++ of security measures. See below. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_CGROUP</constant></term> ++ <listitem><para> ++ Contains the <emphasis>cgroup path</emphasis> of a task, stored ++ as null-terminated string in <varname>item.str</varname>. Its ++ length can also be derived from the item's total size. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_CAPS</constant></term> ++ <listitem><para> ++ Contains sets of <emphasis>capabilities</emphasis>, stored as ++ <type>struct kdbus_caps</type> in <varname>item.caps</varname>. ++ As the item size may increase in the future, programs should be ++ written in a way that it takes ++ <varname>item.caps.last_cap</varname> into account, and derive ++ the number of sets and rows from the item size and the reported ++ number of valid capability bits. ++ <programlisting> ++struct kdbus_caps { ++ __u32 last_cap; ++ __u32 caps[0]; ++}; ++ </programlisting> ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_SECLABEL</constant></term> ++ <listitem><para> ++ Contains the <emphasis>LSM label</emphasis> of a task, stored as ++ null-terminated string in <varname>item.str</varname>. Its length ++ can also be derived from the item's total size. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_AUDIT</constant></term> ++ <listitem><para> ++ Contains the audit <emphasis>sessionid</emphasis> and ++ <emphasis>loginuid</emphasis> of a task, stored as ++ <type>struct kdbus_audit</type> in ++ <varname>item.audit</varname>. ++ <programlisting> ++struct kdbus_audit { ++ __u32 sessionid; ++ __u32 loginuid; ++}; ++ </programlisting> ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_CONN_DESCRIPTION</constant></term> ++ <listitem><para> ++ Contains the <emphasis>connection description</emphasis>, as set ++ by <constant>KDBUS_CMD_HELLO</constant> or ++ <constant>KDBUS_CMD_CONN_UPDATE</constant>, stored as ++ null-terminated string in <varname>item.str</varname>. Its length ++ can also be derived from the item's total size. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ ++ <para> ++ All metadata is automatically translated into the ++ <emphasis>namespaces</emphasis> of the task that receives them. See ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information. ++ </para> ++ ++ <para> ++ [*] Note that the content stored in metadata items of type ++ <constant>KDBUS_ITEM_TID_COMM</constant>, ++ <constant>KDBUS_ITEM_PID_COMM</constant>, ++ <constant>KDBUS_ITEM_EXE</constant> and ++ <constant>KDBUS_ITEM_CMDLINE</constant> ++ can easily be tampered by the sending tasks. Therefore, they should ++ <emphasis>not</emphasis> be used for any sort of security relevant ++ assumptions. The only reason they are transmitted is to let ++ receivers know about details that were set when metadata was ++ collected, even though the task they were collected from is not ++ active any longer when the items are received. ++ </para> ++ </refsect2> ++ ++ <refsect2> ++ <title>Items used for policy entries, matches and notifications</title> ++ ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_POLICY_ACCESS</constant></term> ++ <listitem><para> ++ This item describes a <emphasis>policy access</emphasis> entry to ++ access the policy database of a ++ <citerefentry> ++ <refentrytitle>kdbus.bus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> or ++ <citerefentry> ++ <refentrytitle>kdbus.endpoint</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>. ++ Please refer to ++ <citerefentry> ++ <refentrytitle>kdbus.policy</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information on the policy database and how to access it. ++ <programlisting> ++struct kdbus_policy_access { ++ __u64 type; ++ __u64 access; ++ __u64 id; ++}; ++ </programlisting> ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_ID_ADD</constant></term> ++ <term><constant>KDBUS_ITEM_ID_REMOVE</constant></term> ++ <listitem><para> ++ This item is sent as attachment to a ++ <emphasis>kernel notification</emphasis> and indicates that a ++ new connection was created on the bus, or that a connection was ++ disconnected, respectively. It stores a ++ <type>struct kdbus_notify_id_change</type> in ++ <varname>item.id_change</varname>. ++ The <varname>id</varname> field contains the numeric ID of the ++ connection that was added or removed, and <varname>flags</varname> ++ is set to the connection flags, as passed by ++ <constant>KDBUS_CMD_HELLO</constant>. See ++ <citerefentry> ++ <refentrytitle>kdbus.match</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ and ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information on matches and notification messages. ++ <programlisting> ++struct kdbus_notify_id_change { ++ __u64 id; ++ __u64 flags; ++}; ++ </programlisting> ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_NAME_ADD</constant></term> ++ <term><constant>KDBUS_ITEM_NAME_REMOVE</constant></term> ++ <term><constant>KDBUS_ITEM_NAME_CHANGE</constant></term> ++ <listitem><para> ++ This item is sent as attachment to a ++ <emphasis>kernel notification</emphasis> and indicates that a ++ <emphasis>well-known name</emphasis> appeared, disappeared or ++ transferred to another owner on the bus. It stores a ++ <type>struct kdbus_notify_name_change</type> in ++ <varname>item.name_change</varname>. ++ <varname>old_id</varname> describes the former owner of the name ++ and is set to <constant>0</constant> values in case of ++ <constant>KDBUS_ITEM_NAME_ADD</constant>. ++ <varname>new_id</varname> describes the new owner of the name and ++ is set to <constant>0</constant> values in case of ++ <constant>KDBUS_ITEM_NAME_REMOVE</constant>. ++ The <varname>name</varname> field contains the well-known name the ++ notification is about, as null-terminated string. See ++ <citerefentry> ++ <refentrytitle>kdbus.match</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ and ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information on matches and notification messages. ++ <programlisting> ++struct kdbus_notify_name_change { ++ struct kdbus_notify_id_change old_id; ++ struct kdbus_notify_id_change new_id; ++ char name[0]; ++}; ++ </programlisting> ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_REPLY_TIMEOUT</constant></term> ++ <listitem><para> ++ This item is sent as attachment to a ++ <emphasis>kernel notification</emphasis>. It informs the receiver ++ that an expected reply to a message was not received in time. ++ The remote peer ID and the message cookie is stored in the message ++ header. See ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information about messages, timeouts and notifications. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_REPLY_DEAD</constant></term> ++ <listitem><para> ++ This item is sent as attachment to a ++ <emphasis>kernel notification</emphasis>. It informs the receiver ++ that a remote connection a reply is expected from was disconnected ++ before that reply was sent. The remote peer ID and the message ++ cookie is stored in the message header. See ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information about messages, timeouts and notifications. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect2> ++ </refsect1> ++ ++ <refsect1> ++ <title>See Also</title> ++ <simplelist type="inline"> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.bus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.endpoint</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.fs</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.name</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.pool</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>memfd_create</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry> ++ </member> ++ </simplelist> ++ </refsect1> ++ ++</refentry> +diff --git a/Documentation/kdbus/kdbus.match.xml b/Documentation/kdbus/kdbus.match.xml +new file mode 100644 +index 000000000000..ef77b64e5890 +--- /dev/null ++++ b/Documentation/kdbus/kdbus.match.xml +@@ -0,0 +1,553 @@ ++<?xml version='1.0'?> <!--*-nxml-*--> ++<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" ++ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> ++ ++<refentry id="kdbus.match"> ++ ++ <refentryinfo> ++ <title>kdbus.match</title> ++ <productname>kdbus.match</productname> ++ </refentryinfo> ++ ++ <refmeta> ++ <refentrytitle>kdbus.match</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </refmeta> ++ ++ <refnamediv> ++ <refname>kdbus.match</refname> ++ <refpurpose>kdbus match</refpurpose> ++ </refnamediv> ++ ++ <refsect1> ++ <title>Description</title> ++ ++ <para> ++ kdbus connections can install matches in order to subscribe to signal ++ messages sent on the bus. Such signal messages can be either directed ++ to a single connection (by setting a specific connection ID in ++ <varname>struct kdbus_msg.dst_id</varname> or by sending it to a ++ well-known name), or to potentially <emphasis>all</emphasis> currently ++ active connections on the bus (by setting ++ <varname>struct kdbus_msg.dst_id</varname> to ++ <constant>KDBUS_DST_ID_BROADCAST</constant>). ++ A signal message always has the <constant>KDBUS_MSG_SIGNAL</constant> ++ bit set in the <varname>flags</varname> bitfield. ++ Also, signal messages can originate from either the kernel (called ++ <emphasis>notifications</emphasis>), or from other bus connections. ++ In either case, a bus connection needs to have a suitable ++ <emphasis>match</emphasis> installed in order to receive any signal ++ message. Without any rules installed in the connection, no signal message ++ will be received. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Matches for signal messages from other connections</title> ++ <para> ++ Matches for messages from other connections (not kernel notifications) ++ are implemented as bloom filters (see below). The sender adds certain ++ properties of the message as elements to a bloom filter bit field, and ++ sends that along with the signal message. ++ ++ The receiving connection adds the message properties it is interested in ++ as elements to a bloom mask bit field, and uploads the mask as match rule, ++ possibly along with some other rules to further limit the match. ++ ++ The kernel will match the signal message's bloom filter against the ++ connections bloom mask (simply by &-ing it), and will decide whether ++ the message should be delivered to a connection. ++ </para> ++ <para> ++ The kernel has no notion of any specific properties of the signal message, ++ all it sees are the bit fields of the bloom filter and the 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 signal messages ++ 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 <emphasis>generation</emphasis> 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. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Bloom filters</title> ++ <para> ++ 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 signal messages that have a ++ matching filter. ++ ++ For general information, see ++ <ulink url="https://en.wikipedia.org/wiki/Bloom_filter">the Wikipedia ++ article on bloom filters</ulink>. ++ </para> ++ <para> ++ The size of the bloom filter is defined per bus when it is created, in ++ <varname>kdbus_bloom_parameter.size</varname>. All bloom filters attached ++ to signal 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 in userspace applications. 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 ++ <emphasis>AND</emphasis> logic, but the mask may have more bits set than ++ the filter. Consequently, false positive matches are expected to happen, ++ and programs must deal with that fact by checking the contents of the ++ payload again at receive time. ++ </para> ++ <para> ++ Masks are entities that are always passed to the kernel as part of a ++ match (with an item of type <constant>KDBUS_ITEM_BLOOM_MASK</constant>), ++ and filters can be attached to signals, with an item of type ++ <constant>KDBUS_ITEM_BLOOM_FILTER</constant>. For a filter to match, all ++ its bits have to be set in the match mask as well. ++ </para> ++ <para> ++ For example, consider a bus that has a bloom size of 8 bytes, and the ++ following mask/filter combinations: ++ </para> ++ <programlisting><![CDATA[ ++ filter 0x0101010101010101 ++ mask 0x0101010101010101 ++ -> matches ++ ++ filter 0x0303030303030303 ++ mask 0x0101010101010101 ++ -> doesn't match ++ ++ filter 0x0101010101010101 ++ mask 0x0303030303030303 ++ -> matches ++ ]]></programlisting> ++ ++ <para> ++ Hence, in order to catch all messages, a mask filled with ++ <constant>0xff</constant> bytes can be installed as a wildcard match rule. ++ </para> ++ ++ <refsect2> ++ <title>Generations</title> ++ ++ <para> ++ Uploaded matches may contain multiple masks, which have are as large as ++ the bloom size defined by the bus. Each block of a mask is called a ++ <emphasis>generation</emphasis>, starting at index 0. ++ ++ At match time, when a signal 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 programs to provide backward ++ compatible masks at upload time, while older clients can still match ++ against older versions of filters. ++ </para> ++ </refsect2> ++ </refsect1> ++ ++ <refsect1> ++ <title>Matches for kernel notifications</title> ++ <para> ++ To receive kernel generated notifications (see ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>), ++ a connection must install match rules that are different from ++ the bloom filter matches described in the section above. They can be ++ filtered by the connection ID that caused the notification to be sent, by ++ one of the names it currently owns, or by the type of the notification ++ (ID/name add/remove/change). ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Adding a match</title> ++ <para> ++ To add a match, the <constant>KDBUS_CMD_MATCH_ADD</constant> 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 <emphasis>rule</emphasis>, and the collection of them, ++ which is submitted as one block via the ioctl, is called a ++ <emphasis>match</emphasis>. 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 decrease the chance that the connection's process will be ++ woken up needlessly. ++ ++ 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. ++ </para> ++ ++ <programlisting> ++struct kdbus_cmd_match { ++ __u64 size; ++ __u64 flags; ++ __u64 return_flags; ++ __u64 cookie; ++ struct kdbus_item items[0]; ++}; ++ </programlisting> ++ ++ <para>The fields in this struct are described below.</para> ++ ++ <variablelist> ++ <varlistentry> ++ <term><varname>size</varname></term> ++ <listitem><para> ++ The overall size of the struct, including its items. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>flags</varname></term> ++ <listitem><para>Flags to control the behavior of the ioctl.</para> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_MATCH_REPLACE</constant></term> ++ <listitem> ++ <para>Make the endpoint file group-accessible</para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_FLAG_NEGOTIATE</constant></term> ++ <listitem> ++ <para> ++ Requests a set of valid flags for this ioctl. When this bit is ++ set, no action is taken; the ioctl will return ++ <errorcode>0</errorcode>, and the <varname>flags</varname> ++ field will have all bits set that are valid for this command. ++ The <constant>KDBUS_FLAG_NEGOTIATE</constant> bit will be ++ cleared by the operation. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>return_flags</varname></term> ++ <listitem><para> ++ Flags returned by the kernel. Currently unused and always set to ++ <constant>0</constant> by the kernel. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>cookie</varname></term> ++ <listitem><para> ++ A cookie which identifies the match, so it can be referred to when ++ removing it. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>items</varname></term> ++ <listitem> ++ <para> ++ Items to define the actual rules of the matches. The following item ++ types are expected. Each item will create one new match rule. ++ </para> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_BLOOM_MASK</constant></term> ++ <listitem> ++ <para> ++ 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 the section below for more information on bloom filters. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_NAME</constant></term> ++ <listitem> ++ <para> ++ When used as part of kernel notifications, this item specifies ++ a name that is acquired, lost or that changed its owner (see ++ below). When used as part of a match for user-generated signal ++ messages, it specifies a name that the sending connection must ++ own at the time of sending the signal. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_ID</constant></term> ++ <listitem> ++ <para> ++ Specify a sender connection's ID that will match this rule. ++ For kernel notifications, this specifies the ID of a ++ connection that was added to or removed from the bus. ++ For used-generated signals, it specifies the ID of the ++ connection that sent the signal message. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_NAME_ADD</constant></term> ++ <term><constant>KDBUS_ITEM_NAME_REMOVE</constant></term> ++ <term><constant>KDBUS_ITEM_NAME_CHANGE</constant></term> ++ <listitem> ++ <para> ++ These items request delivery of kernel notifications that ++ describe a name acquisition, loss, or change. The details ++ are stored in the item's ++ <varname>kdbus_notify_name_change</varname> member. ++ All information specified must be matched in order to make ++ the message pass. Use ++ <constant>KDBUS_MATCH_ID_ANY</constant> to ++ match against any unique connection ID. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_ID_ADD</constant></term> ++ <term><constant>KDBUS_ITEM_ID_REMOVE</constant></term> ++ <listitem> ++ <para> ++ These items request delivery of kernel notifications that are ++ generated when a connection is created or terminated. ++ <type>struct kdbus_notify_id_change</type> 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 <constant>KDBUS_MATCH_ID_ANY</constant>, ++ all of them. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_NEGOTIATE</constant></term> ++ <listitem><para> ++ With this item, programs can <emphasis>probe</emphasis> the ++ kernel for known item types. See ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more details. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ ++ <para> ++ Unrecognized items are rejected, and the ioctl will fail with ++ <varname>errno</varname> set to <constant>EINVAL</constant>. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ ++ <para> ++ Refer to ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information on message types. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Removing a match</title> ++ <para> ++ Matches can be removed with the ++ <constant>KDBUS_CMD_MATCH_REMOVE</constant> ioctl, which takes ++ <type>struct kdbus_cmd_match</type> as argument, but its fields ++ usage slightly differs compared to that of ++ <constant>KDBUS_CMD_MATCH_ADD</constant>. ++ </para> ++ ++ <programlisting> ++struct kdbus_cmd_match { ++ __u64 size; ++ __u64 cookie; ++ __u64 flags; ++ __u64 return_flags; ++ struct kdbus_item items[0]; ++}; ++ </programlisting> ++ ++ <para>The fields in this struct are described below.</para> ++ ++ <variablelist> ++ <varlistentry> ++ <term><varname>size</varname></term> ++ <listitem><para> ++ The overall size of the struct, including its items. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>cookie</varname></term> ++ <listitem><para> ++ The cookie of the match, as it was passed when the match was added. ++ All matches that have this cookie will be removed. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>flags</varname></term> ++ <listitem><para> ++ No flags are supported for this use case. ++ <constant>KDBUS_FLAG_NEGOTIATE</constant> is accepted to probe for ++ valid flags. If set, the ioctl will fail with ++ <errorcode>-1</errorcode>, <varname>errno</varname> is set to ++ <constant>EPROTO</constant>, and the <varname>flags</varname> field ++ is set to <constant>0</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>return_flags</varname></term> ++ <listitem><para> ++ Flags returned by the kernel. Currently unused and always set to ++ <constant>0</constant> by the kernel. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>items</varname></term> ++ <listitem> ++ <para> ++ No items are supported for this use case, but ++ <constant>KDBUS_ITEM_NEGOTIATE</constant> is allowed nevertheless. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect1> ++ ++ <refsect1> ++ <title>Return value</title> ++ <para> ++ On success, all mentioned ioctl commands return <errorcode>0</errorcode>; ++ on error, <errorcode>-1</errorcode> is returned, and ++ <varname>errno</varname> is set to indicate the error. ++ If the issued ioctl is illegal for the file descriptor used, ++ <varname>errno</varname> will be set to <constant>ENOTTY</constant>. ++ </para> ++ ++ <refsect2> ++ <title> ++ <constant>KDBUS_CMD_MATCH_ADD</constant> may fail with the following ++ errors ++ </title> ++ ++ <variablelist> ++ <varlistentry> ++ <term><constant>EINVAL</constant></term> ++ <listitem><para> ++ Illegal flags or items. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EDOM</constant></term> ++ <listitem><para> ++ Illegal bloom filter size. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EMFILE</constant></term> ++ <listitem><para> ++ Too many matches for this connection. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect2> ++ ++ <refsect2> ++ <title> ++ <constant>KDBUS_CMD_MATCH_REMOVE</constant> may fail with the following ++ errors ++ </title> ++ ++ <variablelist> ++ <varlistentry> ++ <term><constant>EINVAL</constant></term> ++ <listitem><para> ++ Illegal flags. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EBADSLT</constant></term> ++ <listitem><para> ++ A match entry with the given cookie could not be found. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect2> ++ </refsect1> ++ ++ <refsect1> ++ <title>See Also</title> ++ <simplelist type="inline"> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.bus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.match</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.fs</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.name</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.pool</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ </simplelist> ++ </refsect1> ++</refentry> +diff --git a/Documentation/kdbus/kdbus.message.xml b/Documentation/kdbus/kdbus.message.xml +new file mode 100644 +index 000000000000..c25000dcfbc7 +--- /dev/null ++++ b/Documentation/kdbus/kdbus.message.xml +@@ -0,0 +1,1277 @@ ++<?xml version='1.0'?> <!--*-nxml-*--> ++<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" ++ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> ++ ++<refentry id="kdbus.message"> ++ ++ <refentryinfo> ++ <title>kdbus.message</title> ++ <productname>kdbus.message</productname> ++ </refentryinfo> ++ ++ <refmeta> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </refmeta> ++ ++ <refnamediv> ++ <refname>kdbus.message</refname> ++ <refpurpose>kdbus message</refpurpose> ++ </refnamediv> ++ ++ <refsect1> ++ <title>Description</title> ++ ++ <para> ++ A kdbus message is used to exchange information between two connections ++ on a bus, or to transport notifications from the kernel to one or many ++ connections. This document describes the layout of messages, how payload ++ is added to them and how they are sent and received. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Message layout</title> ++ ++ <para>The layout of a message is shown below.</para> ++ ++ <programlisting> ++ +-------------------------------------------------------------------------+ ++ | 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 | | ++ | | ... | | ++ | +---------------------------------------------------------------------+ | ++ | ... further data records ... | ++ +-------------------------------------------------------------------------+ ++ </programlisting> ++ </refsect1> ++ ++ <refsect1> ++ <title>Message payload</title> ++ ++ <para> ++ 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 <function>mmap()</function>ed by the receiver. See ++ <citerefentry> ++ <refentrytitle>kdbus.pool</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information. ++ </para> ++ ++ <para> ++ Message payload must be described in items attached to a message when ++ it is sent. A receiver can access the payload by looking at the items ++ that are attached to a message in its pool. The following items are used. ++ </para> ++ ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_PAYLOAD_VEC</constant></term> ++ <listitem> ++ <para> ++ This item references a piece of memory on the sender side which is ++ directly copied into the receiver's pool. This 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. ++ This item is never found in a message received by a connection. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_PAYLOAD_OFF</constant></term> ++ <listitem> ++ <para> ++ This item is attached to messages on the receiving side and points ++ to a memory area inside the receiver's pool. The ++ <varname>offset</varname> variable in the item denotes the memory ++ location relative to the message itself. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_PAYLOAD_MEMFD</constant></term> ++ <listitem> ++ <para> ++ Messages can reference <emphasis>memfd</emphasis> 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. ++ </para> ++ <para> ++ Only memfds that have ++ <constant>(F_SEAL_SHRINK|F_SEAL_GROW|F_SEAL_WRITE|F_SEAL_SEAL) ++ </constant> ++ set 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. If those ++ seals are not set on the memfd, the ioctl will fail with ++ <errorcode>-1</errorcode>, and <varname>errno</varname> will be ++ set to <constant>ETXTBUSY</constant>. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_FDS</constant></term> ++ <listitem> ++ <para> ++ Messages can transport regular file descriptors via ++ <constant>KDBUS_ITEM_FDS</constant>. This item carries an array ++ of <type>int</type> values in <varname>item.fd</varname>. The ++ maximum number of file descriptors in the item is ++ <constant>253</constant>, and only one item of this type is ++ accepted per message. All passed values must be valid file ++ descriptors; the open count of each file descriptors is increased ++ by installing it to the receiver's task. This item can only be ++ used for directed messages, not for broadcasts, and only to ++ remote peers that have opted-in for receiving file descriptors ++ at connection time (<constant>KDBUS_HELLO_ACCEPT_FD</constant>). ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ ++ <para> ++ The sender must not make any assumptions on the type in which data is ++ received by the remote peer. The kernel is free to re-pack multiple ++ <constant>KDBUS_ITEM_PAYLOAD_VEC</constant> and ++ <constant>KDBUS_ITEM_PAYLOAD_MEMFD</constant> payloads. For instance, the ++ kernel may decide to merge multiple <constant>VECs</constant> into a ++ single <constant>VEC</constant>, inline <constant>MEMFD</constant> ++ payloads into memory, or merge all passed <constant>VECs</constant> into a ++ single <constant>MEMFD</constant>. However, the kernel preserves the order ++ of passed data. This means that the order of all <constant>VEC</constant> ++ and <constant>MEMFD</constant> items is not changed in respect to each ++ other. In other words: All passed <constant>VEC</constant> and ++ <constant>MEMFD</constant> data payloads are treated as a single stream ++ of data that may be received by the remote peer in a different set of ++ chunks than it was sent as. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Sending messages</title> ++ ++ <para> ++ Messages are passed to the kernel with the ++ <constant>KDBUS_CMD_SEND</constant> ioctl. Depending on the destination ++ address of the message, the kernel delivers the message to the specific ++ destination connection, or to some subset of 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 above). ++ </para> ++ ++ <para> ++ The <constant>KDBUS_CMD_SEND</constant> ioctl uses a ++ <type>struct kdbus_cmd_send</type> to describe the message ++ transfer. ++ </para> ++ <programlisting> ++struct kdbus_cmd_send { ++ __u64 size; ++ __u64 flags; ++ __u64 return_flags; ++ __u64 msg_address; ++ struct kdbus_msg_info reply; ++ struct kdbus_item items[0]; ++}; ++ </programlisting> ++ ++ <para>The fields in this struct are described below.</para> ++ ++ <variablelist> ++ <varlistentry> ++ <term><varname>size</varname></term> ++ <listitem><para> ++ The overall size of the struct, including its items. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>flags</varname></term> ++ <listitem><para>Flags for message delivery</para> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_SEND_SYNC_REPLY</constant></term> ++ <listitem> ++ <para> ++ 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 ++ <constant>KDBUS_SEND_SYNC_REPLY</constant> controls. The ++ <constant>KDBUS_CMD_SEND</constant> 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 ++ <citerefentry> ++ <refentrytitle>signal</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>. ++ ++ The offset of the reply message in the sender's pool is stored ++ in in <varname>offset_reply</varname> when the ioctl has ++ returned without error. Hence, there is no need for another ++ <constant>KDBUS_CMD_RECV</constant> ioctl or anything else to ++ receive the reply. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_FLAG_NEGOTIATE</constant></term> ++ <listitem> ++ <para> ++ Request a set of valid flags for this ioctl. When this bit is ++ set, no action is taken; the ioctl will fail with ++ <errorcode>-1</errorcode>, <varname>errno</varname> ++ is set to <constant>EPROTO</constant>. ++ Once the ioctl returned, the <varname>flags</varname> ++ field will have all bits set that the kernel recognizes as ++ valid for this command. ++ The <constant>KDBUS_FLAG_NEGOTIATE</constant> bit will be ++ cleared by the operation. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>return_flags</varname></term> ++ <listitem><para> ++ Flags returned by the kernel. Currently unused and always set to ++ <constant>0</constant> by the kernel. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>msg_address</varname></term> ++ <listitem><para> ++ In this field, users have to provide a pointer to a message ++ (<type>struct kdbus_msg</type>) to send. See below for a ++ detailed description. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>reply</varname></term> ++ <listitem><para> ++ Only used for synchronous replies. See description of ++ <type>struct kdbus_cmd_recv</type> for more details. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>items</varname></term> ++ <listitem> ++ <para> ++ The following items are currently recognized. ++ </para> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_CANCEL_FD</constant></term> ++ <listitem> ++ <para> ++ When this optional item is passed in, and the call is ++ executed as SYNC call, the passed in file descriptor can be ++ used as alternative cancellation point. The kernel will call ++ <citerefentry> ++ <refentrytitle>poll</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry> ++ on this file descriptor, and once it reports any incoming ++ bytes, the blocking send operation will be canceled; the ++ blocking, synchronous ioctl call will return ++ <errorcode>-1</errorcode>, and <varname>errno</varname> will ++ be set to <errorname>ECANCELED</errorname>. ++ Any type of file descriptor on which ++ <citerefentry> ++ <refentrytitle>poll</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry> ++ can be called on can be used as payload to this item; for ++ example, an eventfd can be used for this purpose, see ++ <citerefentry> ++ <refentrytitle>eventfd</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry>. ++ For asynchronous message sending, this item is allowed but ++ ignored. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ <para> ++ Unrecognized items are rejected, and the ioctl will fail with ++ <varname>errno</varname> set to <constant>EINVAL</constant>. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ ++ <para> ++ The fields in this struct are described below. ++ The message referenced the <varname>msg_address</varname> above has ++ the following layout. ++ </para> ++ ++ <programlisting> ++struct kdbus_msg { ++ __u64 size; ++ __u64 flags; ++ __s64 priority; ++ __u64 dst_id; ++ __u64 src_id; ++ __u64 payload_type; ++ __u64 cookie; ++ __u64 timeout_ns; ++ __u64 cookie_reply; ++ struct kdbus_item items[0]; ++}; ++ </programlisting> ++ ++ <para>The fields in this struct are described below.</para> ++ ++ <variablelist> ++ <varlistentry> ++ <term><varname>size</varname></term> ++ <listitem><para> ++ The overall size of the struct, including its items. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>flags</varname></term> ++ <listitem><para>Flags to describe message details.</para> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_MSG_EXPECT_REPLY</constant></term> ++ <listitem> ++ <para> ++ Expect a reply to this message from the remote peer. 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 ++ <constant>CLOCK_MONOTONIC</constant>. ++ </para><para> ++ For a message to be accepted as reply, it must be a direct ++ message to the original sender (not a broadcast and not a ++ signal message), and its ++ <varname>kdbus_msg.reply_cookie</varname> must match the ++ previous message's <varname>kdbus_msg.cookie</varname>. ++ </para><para> ++ Expected replies also temporarily open the policy of the ++ sending connection, so the other peer is allowed to respond ++ within the given time window. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_MSG_NO_AUTO_START</constant></term> ++ <listitem> ++ <para> ++ By default, when a message is sent to an activator ++ connection, the activator is notified and will start an ++ implementer. This flag inhibits that behavior. With this bit ++ set, and the remote being an activator, the ioctl will fail ++ with <varname>errno</varname> set to ++ <constant>EADDRNOTAVAIL</constant>. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_FLAG_NEGOTIATE</constant></term> ++ <listitem> ++ <para> ++ Requests a set of valid flags for this ioctl. When this bit is ++ set, no action is taken; the ioctl will return ++ <errorcode>0</errorcode>, and the <varname>flags</varname> ++ field will have all bits set that are valid for this command. ++ The <constant>KDBUS_FLAG_NEGOTIATE</constant> bit will be ++ cleared by the operation. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>priority</varname></term> ++ <listitem><para> ++ 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 field ++ should be set to <constant>0</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>dst_id</varname></term> ++ <listitem><para> ++ The numeric ID of the destination connection, or ++ <constant>KDBUS_DST_ID_BROADCAST</constant> ++ (~0ULL) to address every peer on the bus, or ++ <constant>KDBUS_DST_ID_NAME</constant> (0) to look ++ it up dynamically from the bus' name registry. ++ In the latter case, an item of type ++ <constant>KDBUS_ITEM_DST_NAME</constant> is mandatory. ++ Also see ++ <citerefentry> ++ <refentrytitle>kdbus.name</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ . ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>src_id</varname></term> ++ <listitem><para> ++ Upon return of the ioctl, this member will contain the sending ++ connection's numerical ID. Should be 0 at send time. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>payload_type</varname></term> ++ <listitem><para> ++ Type of the payload in the actual data records. Currently, only ++ <constant>KDBUS_PAYLOAD_DBUS</constant> is accepted as input value ++ of this field. When receiving messages that are generated by the ++ kernel (notifications), this field will contain ++ <constant>KDBUS_PAYLOAD_KERNEL</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>cookie</varname></term> ++ <listitem><para> ++ Cookie of this message, for later recognition. Also, when replying ++ to a message (see above), the <varname>cookie_reply</varname> ++ field must match this value. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>timeout_ns</varname></term> ++ <listitem><para> ++ If the message sent requires a reply from the remote peer (see above), ++ this field contains the timeout in absolute nanoseconds based on ++ <constant>CLOCK_MONOTONIC</constant>. Also see ++ <citerefentry> ++ <refentrytitle>clock_gettime</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>cookie_reply</varname></term> ++ <listitem><para> ++ If the message sent is a reply to another message, this field must ++ match the cookie of the formerly received message. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>items</varname></term> ++ <listitem> ++ <para> ++ A dynamically sized list of items to contain additional information. ++ The following items are expected/valid: ++ </para> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_PAYLOAD_VEC</constant></term> ++ <term><constant>KDBUS_ITEM_PAYLOAD_MEMFD</constant></term> ++ <term><constant>KDBUS_ITEM_FDS</constant></term> ++ <listitem> ++ <para> ++ Actual data records containing the payload. See section ++ "Passing of Payload Data". ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_BLOOM_FILTER</constant></term> ++ <listitem> ++ <para> ++ Bloom filter for matches (see below). ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ITEM_DST_NAME</constant></term> ++ <listitem> ++ <para> ++ Well-known name to send this message to. Required if ++ <varname>dst_id</varname> is set to ++ <constant>KDBUS_DST_ID_NAME</constant>. ++ If a connection holding the given name can't be found, ++ the ioctl will fail with <varname>errno</varname> set to ++ <constant>ESRCH</constant> is returned. ++ </para> ++ <para> ++ 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 programs to tie the message ++ sending to the condition that a name is currently owned by a ++ certain unique name. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ ++ <para> ++ The message will be augmented by the requested metadata items when ++ queued into the receiver's pool. See ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ and ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information on metadata. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Receiving messages</title> ++ ++ <para> ++ Messages are received by the client with the ++ <constant>KDBUS_CMD_RECV</constant> ioctl. The endpoint file of the bus ++ supports <function>poll()/epoll()/select()</function>; when new messages ++ are available on the connection's file descriptor, ++ <constant>POLLIN</constant> is reported. For compatibility reasons, ++ <constant>POLLOUT</constant> is always reported as well. Note, however, ++ that the latter does not guarantee that a message can in fact be sent, as ++ this depends on how many pending messages the receiver has in its pool. ++ </para> ++ ++ <para> ++ With the <constant>KDBUS_CMD_RECV</constant> ioctl, a ++ <type>struct kdbus_cmd_recv</type> is used. ++ </para> ++ ++ <programlisting> ++struct kdbus_cmd_recv { ++ __u64 size; ++ __u64 flags; ++ __u64 return_flags; ++ __s64 priority; ++ __u64 dropped_msgs; ++ struct kdbus_msg_info msg; ++ struct kdbus_item items[0]; ++}; ++ </programlisting> ++ ++ <para>The fields in this struct are described below.</para> ++ ++ <variablelist> ++ <varlistentry> ++ <term><varname>size</varname></term> ++ <listitem><para> ++ The overall size of the struct, including its items. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>flags</varname></term> ++ <listitem><para>Flags to control the receive command.</para> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_RECV_PEEK</constant></term> ++ <listitem> ++ <para> ++ 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. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_RECV_DROP</constant></term> ++ <listitem> ++ <para> ++ Drop the next message without doing anything else with it, ++ and free the pool slice. This a short-cut for ++ <constant>KDBUS_RECV_PEEK</constant> and ++ <constant>KDBUS_CMD_FREE</constant>. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_RECV_USE_PRIORITY</constant></term> ++ <listitem> ++ <para> ++ Dequeue the messages ordered by their priority, and filtering ++ them with the priority field (see below). ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_FLAG_NEGOTIATE</constant></term> ++ <listitem> ++ <para> ++ Request a set of valid flags for this ioctl. When this bit is ++ set, no action is taken; the ioctl will fail with ++ <errorcode>-1</errorcode>, <varname>errno</varname> ++ is set to <constant>EPROTO</constant>. ++ Once the ioctl returned, the <varname>flags</varname> ++ field will have all bits set that the kernel recognizes as ++ valid for this command. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>return_flags</varname></term> ++ <listitem><para> ++ Flags returned by the kernel. If the <varname>dropped_msgs</varname> ++ field is non-zero, <constant>KDBUS_RECV_RETURN_DROPPED_MSGS</constant> ++ is set. If a file descriptor could not be installed, the ++ <constant>KDBUS_RECV_RETURN_INCOMPLETE_FDS</constant> flag is set. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>priority</varname></term> ++ <listitem><para> ++ With <constant>KDBUS_RECV_USE_PRIORITY</constant> set in ++ <varname>flags</varname>, messages will be dequeued ordered by their ++ priority, starting with the highest value. Also, messages will be ++ filtered by the value given in this field, so the returned message ++ will at least have the requested priority. If no such message is ++ waiting in the queue, the ioctl will fail, and ++ <varname>errno</varname> will be set to <constant>EAGAIN</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>dropped_msgs</varname></term> ++ <listitem><para> ++ Whenever a message with <constant>KDBUS_MSG_SIGNAL</constant> is sent ++ but cannot be queued on a peer (e.g., as it contains FDs but the peer ++ does not support FDs, or there is no space left in the peer's pool..) ++ the 'dropped_msgs' counter of the peer is incremented. On the next ++ RECV ioctl, the 'dropped_msgs' field is copied into the ioctl struct ++ and cleared on the peer. If it was non-zero, the ++ <constant>KDBUS_RECV_RETURN_DROPPED_MSGS</constant> flag will be set ++ in <varname>return_flags</varname>. Note that this will only happen ++ if the ioctl succeeded or failed with <constant>EAGAIN</constant>. In ++ other error cases, the 'dropped_msgs' field of the peer is left ++ untouched. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>msg</varname></term> ++ <listitem><para> ++ Embedded struct containing information on the received message when ++ this command succeeded (see below). ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>items</varname></term> ++ <listitem><para> ++ Items to specify further details for the receive command. ++ Currently unused, and all items will be rejected with ++ <varname>errno</varname> set to <constant>EINVAL</constant>. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ ++ <para> ++ Both <type>struct kdbus_cmd_recv</type> and ++ <type>struct kdbus_cmd_send</type> embed ++ <type>struct kdbus_msg_info</type>. ++ For the <constant>KDBUS_CMD_SEND</constant> ioctl, it is used to catch ++ synchronous replies, if one was requested, and is unused otherwise. ++ </para> ++ ++ <programlisting> ++struct kdbus_msg_info { ++ __u64 offset; ++ __u64 msg_size; ++ __u64 return_flags; ++}; ++ </programlisting> ++ ++ <para>The fields in this struct are described below.</para> ++ ++ <variablelist> ++ <varlistentry> ++ <term><varname>offset</varname></term> ++ <listitem><para> ++ Upon return of the ioctl, this field contains the offset in the ++ receiver's memory pool. The memory must be freed with ++ <constant>KDBUS_CMD_FREE</constant>. See ++ <citerefentry> ++ <refentrytitle>kdbus.pool</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for further details. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>msg_size</varname></term> ++ <listitem><para> ++ Upon successful return of the ioctl, this field contains the size of ++ the allocated slice at offset <varname>offset</varname>. ++ It is the combination of the size of the stored ++ <type>struct kdbus_msg</type> object plus all appended VECs. ++ You can use it in combination with <varname>offset</varname> to map ++ a single message, instead of mapping the entire pool. See ++ <citerefentry> ++ <refentrytitle>kdbus.pool</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for further details. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>return_flags</varname></term> ++ <listitem> ++ <para> ++ Kernel-provided return flags. Currently, the following flags are ++ defined. ++ </para> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_RECV_RETURN_INCOMPLETE_FDS</constant></term> ++ <listitem> ++ <para> ++ The message contained memfds or file descriptors, and the ++ kernel failed to install one or more of them at receive time. ++ Most probably that happened because the maximum number of ++ file descriptors for the receiver's task were exceeded. ++ In such cases, the message is still delivered, so this is not ++ a fatal condition. File descriptors numbers inside the ++ <constant>KDBUS_ITEM_FDS</constant> item or memfd files ++ referenced by <constant>KDBUS_ITEM_PAYLOAD_MEMFD</constant> ++ items which could not be installed will be set to ++ <constant>-1</constant>. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ ++ <para> ++ Unless <constant>KDBUS_RECV_DROP</constant> was passed, the ++ <varname>offset</varname> field contains the location of the new message ++ inside the receiver's pool after the <constant>KDBUS_CMD_RECV</constant> ++ ioctl was employed. The message is stored as <type>struct kdbus_msg</type> ++ at this offset, and can be interpreted with the semantics described above. ++ </para> ++ <para> ++ Also, if the connection allowed for file descriptor to be passed ++ (<constant>KDBUS_HELLO_ACCEPT_FD</constant>), and if the message contained ++ any, they will be installed into the receiving process when the ++ <constant>KDBUS_CMD_RECV</constant> ioctl is called. ++ <emphasis>memfds</emphasis> may always be part of the message payload. ++ The receiving task is obliged to close all file descriptors appropriately ++ once no longer needed. If <constant>KDBUS_RECV_PEEK</constant> is set, no ++ file descriptors are installed. This allows for peeking at a message, ++ looking at its metadata only and dropping it via ++ <constant>KDBUS_RECV_DROP</constant>, without installing any of the file ++ descriptors into the receiving process. ++ </para> ++ <para> ++ The caller is obliged to call the <constant>KDBUS_CMD_FREE</constant> ++ ioctl with the returned offset when the memory is no longer needed. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Notifications</title> ++ <para> ++ A kernel notification is a regular kdbus message with the following ++ details. ++ </para> ++ ++ <itemizedlist> ++ <listitem><para> ++ kdbus_msg.src_id == <constant>KDBUS_SRC_ID_KERNEL</constant> ++ </para></listitem> ++ <listitem><para> ++ kdbus_msg.dst_id == <constant>KDBUS_DST_ID_BROADCAST</constant> ++ </para></listitem> ++ <listitem><para> ++ kdbus_msg.payload_type == <constant>KDBUS_PAYLOAD_KERNEL</constant> ++ </para></listitem> ++ <listitem><para> ++ Has exactly one of the items attached that are described below. ++ </para></listitem> ++ <listitem><para> ++ Always has a timestamp item (<constant>KDBUS_ITEM_TIMESTAMP</constant>) ++ attached. ++ </para></listitem> ++ </itemizedlist> ++ ++ <para> ++ The kernel will notify its users of the following events. ++ </para> ++ ++ <itemizedlist> ++ <listitem><para> ++ When connection <emphasis>A</emphasis> is terminated while connection ++ <emphasis>B</emphasis> is waiting for a reply from it, connection ++ <emphasis>B</emphasis> is notified with a message with an item of ++ type <constant>KDBUS_ITEM_REPLY_DEAD</constant>. ++ </para></listitem> ++ ++ <listitem><para> ++ When connection <emphasis>A</emphasis> does not receive a reply from ++ connection <emphasis>B</emphasis> within the specified timeout window, ++ connection <emphasis>A</emphasis> will receive a message with an ++ item of type <constant>KDBUS_ITEM_REPLY_TIMEOUT</constant>. ++ </para></listitem> ++ ++ <listitem><para> ++ When an ordinary connection (not a monitor) is created on or removed ++ from a bus, messages with an item of type ++ <constant>KDBUS_ITEM_ID_ADD</constant> or ++ <constant>KDBUS_ITEM_ID_REMOVE</constant>, respectively, are delivered ++ to all bus members that match these messages through their match ++ database. Eavesdroppers (monitor connections) do not cause such ++ notifications to be sent. They are invisible on the bus. ++ </para></listitem> ++ ++ <listitem><para> ++ When a connection gains or loses ownership of a name, messages with an ++ item of type <constant>KDBUS_ITEM_NAME_ADD</constant>, ++ <constant>KDBUS_ITEM_NAME_REMOVE</constant> or ++ <constant>KDBUS_ITEM_NAME_CHANGE</constant> are delivered to all bus ++ members that match these messages through their match database. ++ </para></listitem> ++ </itemizedlist> ++ </refsect1> ++ ++ <refsect1> ++ <title>Return value</title> ++ <para> ++ On success, all mentioned ioctl commands return <errorcode>0</errorcode>; ++ on error, <errorcode>-1</errorcode> is returned, and ++ <varname>errno</varname> is set to indicate the error. ++ If the issued ioctl is illegal for the file descriptor used, ++ <varname>errno</varname> will be set to <constant>ENOTTY</constant>. ++ </para> ++ ++ <refsect2> ++ <title> ++ <constant>KDBUS_CMD_SEND</constant> may fail with the following ++ errors ++ </title> ++ ++ <variablelist> ++ <varlistentry> ++ <term><constant>EOPNOTSUPP</constant></term> ++ <listitem><para> ++ The connection is not an ordinary connection, or the passed ++ file descriptors in <constant>KDBUS_ITEM_FDS</constant> item are ++ either kdbus handles or unix domain sockets. Both are currently ++ unsupported. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EINVAL</constant></term> ++ <listitem><para> ++ The submitted payload type is ++ <constant>KDBUS_PAYLOAD_KERNEL</constant>, ++ <constant>KDBUS_MSG_EXPECT_REPLY</constant> was set without timeout ++ or cookie values, <constant>KDBUS_SEND_SYNC_REPLY</constant> was ++ set without <constant>KDBUS_MSG_EXPECT_REPLY</constant>, an invalid ++ item was supplied, <constant>src_id</constant> was non-zero and was ++ different from the current connection's ID, a supplied memfd had a ++ size of 0, or a string was not properly null-terminated. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>ENOTUNIQ</constant></term> ++ <listitem><para> ++ The supplied destination is ++ <constant>KDBUS_DST_ID_BROADCAST</constant> and either ++ file descriptors were passed, or ++ <constant>KDBUS_MSG_EXPECT_REPLY</constant> was set, ++ or a timeout was given. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>E2BIG</constant></term> ++ <listitem><para> ++ Too many items ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EMSGSIZE</constant></term> ++ <listitem><para> ++ The size of the message header and items or the payload vector ++ is excessive. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EEXIST</constant></term> ++ <listitem><para> ++ Multiple <constant>KDBUS_ITEM_FDS</constant>, ++ <constant>KDBUS_ITEM_BLOOM_FILTER</constant> or ++ <constant>KDBUS_ITEM_DST_NAME</constant> items were supplied. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EBADF</constant></term> ++ <listitem><para> ++ The supplied <constant>KDBUS_ITEM_FDS</constant> or ++ <constant>KDBUS_ITEM_PAYLOAD_MEMFD</constant> items ++ contained an illegal file descriptor. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EMEDIUMTYPE</constant></term> ++ <listitem><para> ++ The supplied memfd is not a sealed kdbus memfd. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EMFILE</constant></term> ++ <listitem><para> ++ Too many file descriptors inside a ++ <constant>KDBUS_ITEM_FDS</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EBADMSG</constant></term> ++ <listitem><para> ++ An item had illegal size, both a <constant>dst_id</constant> and a ++ <constant>KDBUS_ITEM_DST_NAME</constant> was given, or both a name ++ and a bloom filter was given. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>ETXTBSY</constant></term> ++ <listitem><para> ++ The supplied kdbus memfd file cannot be sealed or the seal ++ was removed, because it is shared with other processes or ++ still mapped with ++ <citerefentry> ++ <refentrytitle>mmap</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>ECOMM</constant></term> ++ <listitem><para> ++ A peer does not accept the file descriptors addressed to it. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EFAULT</constant></term> ++ <listitem><para> ++ The supplied bloom filter size was not 64-bit aligned, or supplied ++ memory could not be accessed by the kernel. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EDOM</constant></term> ++ <listitem><para> ++ The supplied bloom filter size did not match the bloom filter ++ size of the bus. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EDESTADDRREQ</constant></term> ++ <listitem><para> ++ <constant>dst_id</constant> was set to ++ <constant>KDBUS_DST_ID_NAME</constant>, but no ++ <constant>KDBUS_ITEM_DST_NAME</constant> was attached. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>ESRCH</constant></term> ++ <listitem><para> ++ The name to look up was not found in the name registry. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EADDRNOTAVAIL</constant></term> ++ <listitem><para> ++ <constant>KDBUS_MSG_NO_AUTO_START</constant> was given but the ++ destination connection is an activator. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>ENXIO</constant></term> ++ <listitem><para> ++ The passed numeric destination connection ID couldn't be found, ++ or is not connected. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>ECONNRESET</constant></term> ++ <listitem><para> ++ The destination connection is no longer active. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>ETIMEDOUT</constant></term> ++ <listitem><para> ++ Timeout while synchronously waiting for a reply. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EINTR</constant></term> ++ <listitem><para> ++ Interrupted system call while synchronously waiting for a reply. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EPIPE</constant></term> ++ <listitem><para> ++ When sending a message, a synchronous reply from the receiving ++ connection was expected but the connection died before answering. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>ENOBUFS</constant></term> ++ <listitem><para> ++ Too many pending messages on the receiver side. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EREMCHG</constant></term> ++ <listitem><para> ++ Both a well-known name and a unique name (ID) was given, but ++ the name is not currently owned by that connection. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EXFULL</constant></term> ++ <listitem><para> ++ The memory pool of the receiver is full. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EREMOTEIO</constant></term> ++ <listitem><para> ++ While synchronously waiting for a reply, the remote peer ++ failed with an I/O error. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect2> ++ ++ <refsect2> ++ <title> ++ <constant>KDBUS_CMD_RECV</constant> may fail with the following ++ errors ++ </title> ++ ++ <variablelist> ++ <varlistentry> ++ <term><constant>EOPNOTSUPP</constant></term> ++ <listitem><para> ++ The connection is not an ordinary connection, or the passed ++ file descriptors are either kdbus handles or unix domain ++ sockets. Both are currently unsupported. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EINVAL</constant></term> ++ <listitem><para> ++ Invalid flags or offset. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EAGAIN</constant></term> ++ <listitem><para> ++ No message found in the queue ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect2> ++ </refsect1> ++ ++ <refsect1> ++ <title>See Also</title> ++ <simplelist type="inline"> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.bus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.endpoint</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.fs</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.name</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.pool</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>clock_gettime</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>ioctl</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>poll</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>select</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>epoll</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>eventfd</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>memfd_create</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry> ++ </member> ++ </simplelist> ++ </refsect1> ++</refentry> +diff --git a/Documentation/kdbus/kdbus.name.xml b/Documentation/kdbus/kdbus.name.xml +new file mode 100644 +index 000000000000..3f5f6a6c5ed6 +--- /dev/null ++++ b/Documentation/kdbus/kdbus.name.xml +@@ -0,0 +1,711 @@ ++<?xml version='1.0'?> <!--*-nxml-*--> ++<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" ++ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> ++ ++<refentry id="kdbus.name"> ++ ++ <refentryinfo> ++ <title>kdbus.name</title> ++ <productname>kdbus.name</productname> ++ </refentryinfo> ++ ++ <refmeta> ++ <refentrytitle>kdbus.name</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </refmeta> ++ ++ <refnamediv> ++ <refname>kdbus.name</refname> ++ <refpurpose>kdbus.name</refpurpose> ++ </refnamediv> ++ ++ <refsect1> ++ <title>Description</title> ++ <para> ++ Each ++ <citerefentry> ++ <refentrytitle>kdbus.bus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ 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 <varname>kdbus_msg.dst_id</varname> set to ++ <constant>KDBUS_DST_ID_NAME</constant>, or when a registry dump is ++ requested with <constant>KDBUS_CMD_NAME_LIST</constant>. ++ </para> ++ ++ <para> ++ All of the below is subject to policy rules for <emphasis>SEE</emphasis> ++ and <emphasis>OWN</emphasis> permissions. See ++ <citerefentry> ++ <refentrytitle>kdbus.policy</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Name validity</title> ++ <para> ++ A name has to comply with the following rules in order to be considered ++ valid. ++ </para> ++ ++ <itemizedlist> ++ <listitem> ++ <para> ++ The name has two or more elements separated by a ++ '<literal>.</literal>' (period) character. ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ All elements must contain at least one character. ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ Each element must only contain the ASCII characters ++ <literal>[A-Z][a-z][0-9]_</literal> and must not begin with a ++ digit. ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ The name must contain at least one '<literal>.</literal>' (period) ++ character (and thus at least two elements). ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ The name must not begin with a '<literal>.</literal>' (period) ++ character. ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ The name must not exceed <constant>255</constant> characters in ++ length. ++ </para> ++ </listitem> ++ </itemizedlist> ++ </refsect1> ++ ++ <refsect1> ++ <title>Acquiring a name</title> ++ <para> ++ To acquire a name, a client uses the ++ <constant>KDBUS_CMD_NAME_ACQUIRE</constant> ioctl with ++ <type>struct kdbus_cmd</type> as argument. ++ </para> ++ ++ <programlisting> ++struct kdbus_cmd { ++ __u64 size; ++ __u64 flags; ++ __u64 return_flags; ++ struct kdbus_item items[0]; ++}; ++ </programlisting> ++ ++ <para>The fields in this struct are described below.</para> ++ ++ <variablelist> ++ <varlistentry> ++ <term><varname>size</varname></term> ++ <listitem><para> ++ The overall size of the struct, including its items. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>flags</varname></term> ++ <listitem><para>Flags to control details in the name acquisition.</para> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_NAME_REPLACE_EXISTING</constant></term> ++ <listitem> ++ <para> ++ Acquiring a name that is already present usually fails, ++ unless this flag is set in the call, and ++ <constant>KDBUS_NAME_ALLOW_REPLACEMENT</constant> (see below) ++ was set when the current owner of the name acquired it, or ++ if the current owner is an activator connection (see ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>). ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_NAME_ALLOW_REPLACEMENT</constant></term> ++ <listitem> ++ <para> ++ Allow other connections to take over this name. When this ++ happens, the former owner of the connection will be notified ++ of the name loss. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_NAME_QUEUE</constant></term> ++ <listitem> ++ <para> ++ A name that is already acquired by a connection can not be ++ acquired again (unless the ++ <constant>KDBUS_NAME_ALLOW_REPLACEMENT</constant> flag was ++ set during acquisition; see above). ++ 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. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_FLAG_NEGOTIATE</constant></term> ++ <listitem> ++ <para> ++ Request a set of valid flags for this ioctl. When this bit is ++ set, no action is taken; the ioctl will fail with ++ <errorcode>-1</errorcode>, and <varname>errno</varname> ++ is set to <constant>EPROTO</constant>. ++ Once the ioctl returned, the <varname>flags</varname> ++ field will have all bits set that the kernel recognizes as ++ valid for this command. ++ The <constant>KDBUS_FLAG_NEGOTIATE</constant> bit will be ++ cleared by the operation. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>return_flags</varname></term> ++ <listitem> ++ <para> ++ Flags returned by the kernel. Currently, the following may be ++ returned by the kernel. ++ </para> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_NAME_IN_QUEUE</constant></term> ++ <listitem> ++ <para> ++ The name was not acquired yet, but the connection was ++ placed in the queue of peers waiting for the name. ++ This can only happen if <constant>KDBUS_NAME_QUEUE</constant> ++ was set in the <varname>flags</varname> member (see above). ++ The connection will receive a name owner change notification ++ once the current owner has given up the name and its ++ ownership was transferred. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>items</varname></term> ++ <listitem> ++ <para> ++ Items to submit the name. Currently, one item of type ++ <constant>KDBUS_ITEM_NAME</constant> is expected and allowed, and ++ the contained string must be a valid bus name. ++ <constant>KDBUS_ITEM_NEGOTIATE</constant> may be used to probe for ++ valid item types. See ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for a detailed description of how this item is used. ++ </para> ++ <para> ++ Unrecognized items are rejected, and the ioctl will fail with ++ <varname>errno</varname> set to <errorname>>EINVAL</errorname>. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect1> ++ ++ <refsect1> ++ <title>Releasing a name</title> ++ <para> ++ A connection may release a name explicitly with the ++ <constant>KDBUS_CMD_NAME_RELEASE</constant> ioctl. If the connection was ++ an implementer 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 first one in the queue (the oldest entry) will become ++ the new owner. The same happens implicitly for all names once a ++ connection terminates. See ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information on connections. ++ </para> ++ <para> ++ The <constant>KDBUS_CMD_NAME_RELEASE</constant> ioctl uses the same data ++ structure as the acquisition call ++ (<constant>KDBUS_CMD_NAME_ACQUIRE</constant>), ++ but with slightly different field usage. ++ </para> ++ ++ <programlisting> ++struct kdbus_cmd { ++ __u64 size; ++ __u64 flags; ++ __u64 return_flags; ++ struct kdbus_item items[0]; ++}; ++ </programlisting> ++ ++ <para>The fields in this struct are described below.</para> ++ ++ <variablelist> ++ <varlistentry> ++ <term><varname>size</varname></term> ++ <listitem><para> ++ The overall size of the struct, including its items. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>flags</varname></term> ++ <listitem><para> ++ Flags to the command. Currently unused. ++ <constant>KDBUS_FLAG_NEGOTIATE</constant> is accepted to probe for ++ valid flags. If set, the ioctl will return <errorcode>0</errorcode>, ++ and the <varname>flags</varname> field is set to ++ <constant>0</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>return_flags</varname></term> ++ <listitem><para> ++ Flags returned by the kernel. Currently unused and always set to ++ <constant>0</constant> by the kernel. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>items</varname></term> ++ <listitem> ++ <para> ++ Items to submit the name. Currently, one item of type ++ <constant>KDBUS_ITEM_NAME</constant> is expected and allowed, and ++ the contained string must be a valid bus name. ++ <constant>KDBUS_ITEM_NEGOTIATE</constant> may be used to probe for ++ valid item types. See ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for a detailed description of how this item is used. ++ </para> ++ <para> ++ Unrecognized items are rejected, and the ioctl will fail with ++ <varname>errno</varname> set to <constant>EINVAL</constant>. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect1> ++ ++ <refsect1> ++ <title>Dumping the name registry</title> ++ <para> ++ A connection may request a complete or filtered dump of currently active ++ bus names with the <constant>KDBUS_CMD_LIST</constant> ioctl, which ++ takes a <type>struct kdbus_cmd_list</type> as argument. ++ </para> ++ ++ <programlisting> ++struct kdbus_cmd_list { ++ __u64 flags; ++ __u64 return_flags; ++ __u64 offset; ++}; ++ </programlisting> ++ ++ <para>The fields in this struct are described below.</para> ++ ++ <variablelist> ++ <varlistentry> ++ <term><varname>flags</varname></term> ++ <listitem> ++ <para> ++ Any combination of flags to specify which names should be dumped. ++ </para> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_LIST_UNIQUE</constant></term> ++ <listitem> ++ <para> ++ List the unique (numeric) IDs of the connection, whether it ++ owns a name or not. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_LIST_NAMES</constant></term> ++ <listitem> ++ <para> ++ List well-known names stored in the database which are ++ actively owned by a real connection (not an activator). ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_LIST_ACTIVATORS</constant></term> ++ <listitem> ++ <para> ++ List names that are owned by an activator. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_LIST_QUEUED</constant></term> ++ <listitem> ++ <para> ++ List connections that are not yet owning a name but are ++ waiting for it to become available. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_FLAG_NEGOTIATE</constant></term> ++ <listitem> ++ <para> ++ Request a set of valid flags for this ioctl. When this bit is ++ set, no action is taken; the ioctl will fail with ++ <errorcode>-1</errorcode>, and <varname>errno</varname> ++ is set to <constant>EPROTO</constant>. ++ Once the ioctl returned, the <varname>flags</varname> ++ field will have all bits set that the kernel recognizes as ++ valid for this command. ++ The <constant>KDBUS_FLAG_NEGOTIATE</constant> bit will be ++ cleared by the operation. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>return_flags</varname></term> ++ <listitem><para> ++ Flags returned by the kernel. Currently unused and always set to ++ <constant>0</constant> by the kernel. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>offset</varname></term> ++ <listitem><para> ++ When the ioctl returns successfully, the offset to the name registry ++ dump inside the connection's pool will be stored in this field. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ ++ <para> ++ The returned list of names is stored in a <type>struct kdbus_list</type> ++ that in turn contains an array of type <type>struct kdbus_info</type>, ++ The array-size in bytes is given as <varname>list_size</varname>. ++ The fields inside <type>struct kdbus_info</type> is described next. ++ </para> ++ ++ <programlisting> ++struct kdbus_info { ++ __u64 size; ++ __u64 id; ++ __u64 flags; ++ struct kdbus_item items[0]; ++}; ++ </programlisting> ++ ++ <para>The fields in this struct are described below.</para> ++ ++ <variablelist> ++ <varlistentry> ++ <term><varname>size</varname></term> ++ <listitem><para> ++ The overall size of the struct, including its items. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>id</varname></term> ++ <listitem><para> ++ The owning connection's unique ID. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>flags</varname></term> ++ <listitem><para> ++ The flags of the owning connection. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>items</varname></term> ++ <listitem> ++ <para> ++ Items containing the actual name. Currently, one item of type ++ <constant>KDBUS_ITEM_OWNED_NAME</constant> will be attached, ++ including the name's flags. In that item, the flags field of the ++ name may carry the following bits: ++ </para> ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_NAME_ALLOW_REPLACEMENT</constant></term> ++ <listitem> ++ <para> ++ Other connections are allowed to take over this name from the ++ connection that owns it. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_NAME_IN_QUEUE</constant></term> ++ <listitem> ++ <para> ++ When retrieving a list of currently acquired names in the ++ registry, this flag indicates whether the connection ++ actually owns the name or is currently waiting for it to ++ become available. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_NAME_ACTIVATOR</constant></term> ++ <listitem> ++ <para> ++ An activator connection owns a name as a placeholder for an ++ implementer, which is started on demand by programs as soon ++ as the first message arrives. There's some more information ++ on this topic in ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ . ++ </para> ++ <para> ++ In contrast to ++ <constant>KDBUS_NAME_REPLACE_EXISTING</constant>, ++ 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 implementer connection terminates. ++ </para> ++ <para> ++ This flag can not be used when acquiring a name, but is ++ implicitly set through <constant>KDBUS_CMD_HELLO</constant> ++ with <constant>KDBUS_HELLO_ACTIVATOR</constant> set in ++ <varname>kdbus_cmd_hello.conn_flags</varname>. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_FLAG_NEGOTIATE</constant></term> ++ <listitem> ++ <para> ++ Requests a set of valid flags for this ioctl. When this bit is ++ set, no action is taken; the ioctl will return ++ <errorcode>0</errorcode>, and the <varname>flags</varname> ++ field will have all bits set that are valid for this command. ++ The <constant>KDBUS_FLAG_NEGOTIATE</constant> bit will be ++ cleared by the operation. ++ </para> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ </listitem> ++ </varlistentry> ++ </variablelist> ++ ++ <para> ++ The returned buffer must be freed with the ++ <constant>KDBUS_CMD_FREE</constant> ioctl when the user is finished with ++ it. See ++ <citerefentry> ++ <refentrytitle>kdbus.pool</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Return value</title> ++ <para> ++ On success, all mentioned ioctl commands return <errorcode>0</errorcode>; ++ on error, <errorcode>-1</errorcode> is returned, and ++ <varname>errno</varname> is set to indicate the error. ++ If the issued ioctl is illegal for the file descriptor used, ++ <varname>errno</varname> will be set to <constant>ENOTTY</constant>. ++ </para> ++ ++ <refsect2> ++ <title> ++ <constant>KDBUS_CMD_NAME_ACQUIRE</constant> may fail with the following ++ errors ++ </title> ++ ++ <variablelist> ++ <varlistentry> ++ <term><constant>EINVAL</constant></term> ++ <listitem><para> ++ Illegal command flags, illegal name provided, or an activator ++ tried to acquire a second name. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EPERM</constant></term> ++ <listitem><para> ++ Policy prohibited name ownership. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EALREADY</constant></term> ++ <listitem><para> ++ Connection already owns that name. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EEXIST</constant></term> ++ <listitem><para> ++ The name already exists and can not be taken over. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>E2BIG</constant></term> ++ <listitem><para> ++ The maximum number of well-known names per connection is exhausted. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect2> ++ ++ <refsect2> ++ <title> ++ <constant>KDBUS_CMD_NAME_RELEASE</constant> ++ may fail with the following errors ++ </title> ++ ++ <variablelist> ++ <varlistentry> ++ <term><constant>EINVAL</constant></term> ++ <listitem><para> ++ Invalid command flags, or invalid name provided. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>ESRCH</constant></term> ++ <listitem><para> ++ Name is not found in the registry. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EADDRINUSE</constant></term> ++ <listitem><para> ++ Name is owned by a different connection and can't be released. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect2> ++ ++ <refsect2> ++ <title> ++ <constant>KDBUS_CMD_LIST</constant> may fail with the following ++ errors ++ </title> ++ ++ <variablelist> ++ <varlistentry> ++ <term><constant>EINVAL</constant></term> ++ <listitem><para> ++ Invalid command flags ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>ENOBUFS</constant></term> ++ <listitem><para> ++ No available memory in the connection's pool. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect2> ++ </refsect1> ++ ++ <refsect1> ++ <title>See Also</title> ++ <simplelist type="inline"> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.bus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.policy</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.pool</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ </simplelist> ++ </refsect1> ++</refentry> +diff --git a/Documentation/kdbus/kdbus.policy.xml b/Documentation/kdbus/kdbus.policy.xml +new file mode 100644 +index 000000000000..67324163880a +--- /dev/null ++++ b/Documentation/kdbus/kdbus.policy.xml +@@ -0,0 +1,406 @@ ++<?xml version='1.0'?> <!--*-nxml-*--> ++<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" ++ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> ++ ++<refentry id="kdbus.policy"> ++ ++ <refentryinfo> ++ <title>kdbus.policy</title> ++ <productname>kdbus.policy</productname> ++ </refentryinfo> ++ ++ <refmeta> ++ <refentrytitle>kdbus.policy</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </refmeta> ++ ++ <refnamediv> ++ <refname>kdbus.policy</refname> ++ <refpurpose>kdbus policy</refpurpose> ++ </refnamediv> ++ ++ <refsect1> ++ <title>Description</title> ++ ++ <para> ++ A kdbus policy restricts the possibilities of connections to own, see and ++ talk to well-known names. A policy can be associated with a bus (through a ++ policy holder connection) or a custom endpoint. kdbus stores its policy ++ information in a database that can be accessed through the following ++ ioctl commands: ++ </para> ++ ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_CMD_HELLO</constant></term> ++ <listitem><para> ++ When creating, or updating, a policy holder connection. See ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_CMD_ENDPOINT_MAKE</constant></term> ++ <term><constant>KDBUS_CMD_ENDPOINT_UPDATE</constant></term> ++ <listitem><para> ++ When creating, or updating, a bus custom endpoint. See ++ <citerefentry> ++ <refentrytitle>kdbus.endpoint</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ ++ <para> ++ In all cases, the name and policy access information is stored in items ++ of type <constant>KDBUS_ITEM_NAME</constant> and ++ <constant>KDBUS_ITEM_POLICY_ACCESS</constant>. For this transport, the ++ following rules apply. ++ </para> ++ ++ <itemizedlist> ++ <listitem> ++ <para> ++ An item of type <constant>KDBUS_ITEM_NAME</constant> must be followed ++ by at least one <constant>KDBUS_ITEM_POLICY_ACCESS</constant> item. ++ </para> ++ </listitem> ++ ++ <listitem> ++ <para> ++ An item of type <constant>KDBUS_ITEM_NAME</constant> can be followed ++ by an arbitrary number of ++ <constant>KDBUS_ITEM_POLICY_ACCESS</constant> items. ++ </para> ++ </listitem> ++ ++ <listitem> ++ <para> ++ An arbitrary number of groups of names and access levels can be given. ++ </para> ++ </listitem> ++ </itemizedlist> ++ ++ <para> ++ Names passed in items of type <constant>KDBUS_ITEM_NAME</constant> must ++ comply to the rules of valid kdbus.name. See ++ <citerefentry> ++ <refentrytitle>kdbus.name</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information. ++ ++ The payload of an item of type ++ <constant>KDBUS_ITEM_POLICY_ACCESS</constant> is defined by the following ++ struct. For more information on the layout of items, please refer to ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>. ++ </para> ++ ++ <programlisting> ++struct kdbus_policy_access { ++ __u64 type; ++ __u64 access; ++ __u64 id; ++}; ++ </programlisting> ++ ++ <para>The fields in this struct are described below.</para> ++ ++ <variablelist> ++ <varlistentry> ++ <term><varname>type</varname></term> ++ <listitem> ++ <para> ++ One of the following. ++ </para> ++ ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_POLICY_ACCESS_USER</constant></term> ++ <listitem><para> ++ Grant access to a user with the UID stored in the ++ <varname>id</varname> field. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_POLICY_ACCESS_GROUP</constant></term> ++ <listitem><para> ++ Grant access to a user with the GID stored in the ++ <varname>id</varname> field. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_POLICY_ACCESS_WORLD</constant></term> ++ <listitem><para> ++ Grant access to everyone. The <varname>id</varname> field ++ is ignored. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>access</varname></term> ++ <listitem> ++ <para> ++ The access to grant. One of the following. ++ </para> ++ ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_POLICY_SEE</constant></term> ++ <listitem><para> ++ Allow the name to be seen. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_POLICY_TALK</constant></term> ++ <listitem><para> ++ Allow the name to be talked to. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_POLICY_OWN</constant></term> ++ <listitem><para> ++ Allow the name to be owned. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>id</varname></term> ++ <listitem><para> ++ For <constant>KDBUS_POLICY_ACCESS_USER</constant>, stores the UID. ++ For <constant>KDBUS_POLICY_ACCESS_GROUP</constant>, stores the GID. ++ </para></listitem> ++ </varlistentry> ++ ++ </variablelist> ++ ++ <para> ++ All endpoints of buses have an empty policy database by default. ++ Therefore, unless policy rules are added, all operations will also be ++ denied by default. Also see ++ <citerefentry> ++ <refentrytitle>kdbus.endpoint</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Wildcard names</title> ++ <para> ++ Policy holder connections may upload names that contain the wildcard ++ suffix (<literal>".*"</literal>). Such a policy entry is effective for ++ every well-known name that extends the provided name by exactly one more ++ level. ++ ++ For example, the name <literal>foo.bar.*</literal> matches both ++ <literal>"foo.bar.baz"</literal> and ++ <literal>"foo.bar.bazbaz"</literal> are, but not ++ <literal>"foo.bar.baz.baz"</literal>. ++ ++ 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. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Privileged connections</title> ++ <para> ++ The policy database is overruled when action is taken by a privileged ++ connection. Please refer to ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information on what makes a connection privileged. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Examples</title> ++ <para> ++ For instance, a set of policy rules may look like this: ++ </para> ++ ++ <programlisting> ++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 ++ </programlisting> ++ ++ <para> ++ 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. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>TALK access and multiple well-known names per connection</title> ++ <para> ++ Note that TALK access is checked against all names of a connection. For ++ example, if a connection owns both <constant>'org.foo.bar'</constant> and ++ <constant>'org.blah.baz'</constant>, and the policy database allows ++ <constant>'org.blah.baz'</constant> to be talked to by WORLD, then this ++ permission is also granted to <constant>'org.foo.bar'</constant>. That ++ might sound illogical, but after all, we allow messages to be directed to ++ either the ID 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. ++ ++ For broadcast messages, the receiver needs TALK permissions to the sender ++ to receive the broadcast. ++ </para> ++ <para> ++ Both the endpoint and the bus policy databases are consulted to allow ++ name registry listing, owning a well-known name and message delivery. ++ If either one fails, the operation is failed with ++ <varname>errno</varname> set to <constant>EPERM</constant>. ++ ++ 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. ++ ++ Also see ++ <citerefentry> ++ <refentrytitle>kdbus.match</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Implicit policies</title> ++ <para> ++ Depending on the type of the endpoint, a set of implicit rules that ++ override installed policies might be enforced. ++ ++ On default endpoints, the following set is enforced and checked before ++ any user-supplied policy is checked. ++ </para> ++ ++ <itemizedlist> ++ <listitem> ++ <para> ++ Privileged connections always override any installed policy. Those ++ connections could easily install their own policies, so there is no ++ reason to enforce installed policies. ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ Connections can always talk to connections of the same user. This ++ includes broadcast messages. ++ </para> ++ </listitem> ++ </itemizedlist> ++ ++ <para> ++ Custom endpoints have stricter policies. The following rules apply: ++ </para> ++ ++ <itemizedlist> ++ <listitem> ++ <para> ++ Policy rules are always enforced, even if the connection is a ++ privileged connection. ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ Policy rules are always enforced for <constant>TALK</constant> access, ++ even if both ends are running under the same user. This includes ++ broadcast messages. ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ To restrict the set of names that can be seen, endpoint policies can ++ install <constant>SEE</constant> policies. ++ </para> ++ </listitem> ++ </itemizedlist> ++ </refsect1> ++ ++ <refsect1> ++ <title>See Also</title> ++ <simplelist type="inline"> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.bus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.endpoint</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.fs</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.name</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.pool</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ </simplelist> ++ </refsect1> ++</refentry> +diff --git a/Documentation/kdbus/kdbus.pool.xml b/Documentation/kdbus/kdbus.pool.xml +new file mode 100644 +index 000000000000..05fd01902ad4 +--- /dev/null ++++ b/Documentation/kdbus/kdbus.pool.xml +@@ -0,0 +1,320 @@ ++<?xml version='1.0'?> <!--*-nxml-*--> ++<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" ++ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> ++ ++<refentry id="kdbus.pool"> ++ ++ <refentryinfo> ++ <title>kdbus.pool</title> ++ <productname>kdbus.pool</productname> ++ </refentryinfo> ++ ++ <refmeta> ++ <refentrytitle>kdbus.pool</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </refmeta> ++ ++ <refnamediv> ++ <refname>kdbus.pool</refname> ++ <refpurpose>kdbus pool</refpurpose> ++ </refnamediv> ++ ++ <refsect1> ++ <title>Description</title> ++ <para> ++ A pool for data received from the kernel is installed for every ++ <emphasis>connection</emphasis> of the <emphasis>bus</emphasis>, and ++ is sized according to the information stored in the ++ <varname>pool_size</varname> member of <type>struct kdbus_cmd_hello</type> ++ when <constant>KDBUS_CMD_HELLO</constant> is employed. Internally, the ++ pool is segmented into <emphasis>slices</emphasis>, each referenced by its ++ <emphasis>offset</emphasis> in the pool, expressed in <type>bytes</type>. ++ See ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more information about <constant>KDBUS_CMD_HELLO</constant>. ++ </para> ++ ++ <para> ++ The pool is written to by the kernel when one of the following ++ <emphasis>ioctls</emphasis> is issued: ++ ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_CMD_HELLO</constant></term> ++ <listitem><para> ++ ... to receive details about the bus the connection was made to ++ </para></listitem> ++ </varlistentry> ++ <varlistentry> ++ <term><constant>KDBUS_CMD_RECV</constant></term> ++ <listitem><para> ++ ... to receive a message ++ </para></listitem> ++ </varlistentry> ++ <varlistentry> ++ <term><constant>KDBUS_CMD_LIST</constant></term> ++ <listitem><para> ++ ... to dump the name registry ++ </para></listitem> ++ </varlistentry> ++ <varlistentry> ++ <term><constant>KDBUS_CMD_CONN_INFO</constant></term> ++ <listitem><para> ++ ... to retrieve information on a connection ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ ++ </para> ++ <para> ++ The <varname>offset</varname> fields returned by either one of the ++ aforementioned ioctls describe offsets inside the pool. In order to make ++ the slice available for subsequent calls, ++ <constant>KDBUS_CMD_FREE</constant> has to be called on that offset ++ (see below). Otherwise, the pool will fill up, and the connection won't ++ be able to receive any more information through its pool. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Pool slice allocation</title> ++ <para> ++ Pool slices are allocated by the kernel in order to report information ++ back to a task, such as messages, returned name list etc. ++ Allocation of pool slices cannot be initiated by userspace. See ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ and ++ <citerefentry> ++ <refentrytitle>kdbus.name</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for examples of commands that use the <emphasis>pool</emphasis> to ++ return data. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Accessing the pool memory</title> ++ <para> ++ Memory in the pool is read-only for userspace and may only be written ++ to by the kernel. To read from the pool memory, the caller is expected to ++ <citerefentry> ++ <refentrytitle>mmap</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry> ++ the buffer into its task, like this: ++ </para> ++ <programlisting> ++uint8_t *buf = mmap(NULL, size, PROT_READ, MAP_SHARED, conn_fd, 0); ++ </programlisting> ++ ++ <para> ++ In order to map the entire pool, the <varname>size</varname> parameter in ++ the example above should be set to the value of the ++ <varname>pool_size</varname> member of ++ <type>struct kdbus_cmd_hello</type> when ++ <constant>KDBUS_CMD_HELLO</constant> was employed to create the ++ connection (see above). ++ </para> ++ ++ <para> ++ The <emphasis>file descriptor</emphasis> used to map the memory must be ++ the one that was used to create the <emphasis>connection</emphasis>. ++ In other words, the one that was used to call ++ <constant>KDBUS_CMD_HELLO</constant>. See ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more details. ++ </para> ++ ++ <para> ++ Alternatively, instead of mapping the entire pool buffer, only parts ++ of it can be mapped. Every kdbus command that returns an ++ <emphasis>offset</emphasis> (see above) also reports a ++ <emphasis>size</emphasis> along with it, so programs can be written ++ in a way that it only maps portions of the pool to access a specific ++ <emphasis>slice</emphasis>. ++ </para> ++ ++ <para> ++ When access to the pool memory is no longer needed, programs should ++ call <function>munmap()</function> on the pointer returned by ++ <function>mmap()</function>. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Freeing pool slices</title> ++ <para> ++ The <constant>KDBUS_CMD_FREE</constant> ioctl is used to free a slice ++ inside the pool, describing an offset that was returned in an ++ <varname>offset</varname> field of another ioctl struct. ++ The <constant>KDBUS_CMD_FREE</constant> command takes a ++ <type>struct kdbus_cmd_free</type> as argument. ++ </para> ++ ++<programlisting> ++struct kdbus_cmd_free { ++ __u64 size; ++ __u64 flags; ++ __u64 return_flags; ++ __u64 offset; ++ struct kdbus_item items[0]; ++}; ++</programlisting> ++ ++ <para>The fields in this struct are described below.</para> ++ ++ <variablelist> ++ <varlistentry> ++ <term><varname>size</varname></term> ++ <listitem><para> ++ The overall size of the struct, including its items. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>flags</varname></term> ++ <listitem><para> ++ Currently unused. ++ <constant>KDBUS_FLAG_NEGOTIATE</constant> is accepted to probe for ++ valid flags. If set, the ioctl will return <errorcode>0</errorcode>, ++ and the <varname>flags</varname> field is set to ++ <constant>0</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>return_flags</varname></term> ++ <listitem><para> ++ Flags returned by the kernel. Currently unused and always set to ++ <constant>0</constant> by the kernel. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>offset</varname></term> ++ <listitem><para> ++ The offset to free, as returned by other ioctls that allocated ++ memory for returned information. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><varname>items</varname></term> ++ <listitem><para> ++ Items to specify further details for the receive command. ++ Currently unused. ++ Unrecognized items are rejected, and the ioctl will fail with ++ <varname>errno</varname> set to <constant>EINVAL</constant>. ++ All items except for ++ <constant>KDBUS_ITEM_NEGOTIATE</constant> (see ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ ) will be rejected. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect1> ++ ++ <refsect1> ++ <title>Return value</title> ++ <para> ++ On success, all mentioned ioctl commands return <errorcode>0</errorcode>; ++ on error, <errorcode>-1</errorcode> is returned, and ++ <varname>errno</varname> is set to indicate the error. ++ If the issued ioctl is illegal for the file descriptor used, ++ <varname>errno</varname> will be set to <constant>ENOTTY</constant>. ++ </para> ++ ++ <refsect2> ++ <title> ++ <constant>KDBUS_CMD_FREE</constant> may fail with the following ++ errors ++ </title> ++ ++ <variablelist> ++ <varlistentry> ++ <term><constant>ENXIO</constant></term> ++ <listitem><para> ++ No pool slice found at given offset. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EINVAL</constant></term> ++ <listitem><para> ++ Invalid flags provided. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>EINVAL</constant></term> ++ <listitem><para> ++ The offset is valid, but the user is not allowed to free the slice. ++ This happens, for example, if the offset was retrieved with ++ <constant>KDBUS_RECV_PEEK</constant>. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ </refsect2> ++ </refsect1> ++ ++ <refsect1> ++ <title>See Also</title> ++ <simplelist type="inline"> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.bus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.endpoint</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.name</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>mmap</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>munmap</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry> ++ </member> ++ </simplelist> ++ </refsect1> ++</refentry> +diff --git a/Documentation/kdbus/kdbus.xml b/Documentation/kdbus/kdbus.xml +new file mode 100644 +index 000000000000..194abd2e76cc +--- /dev/null ++++ b/Documentation/kdbus/kdbus.xml +@@ -0,0 +1,1012 @@ ++<?xml version='1.0'?> <!--*-nxml-*--> ++<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" ++ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> ++ ++<refentry id="kdbus"> ++ ++ <refentryinfo> ++ <title>kdbus</title> ++ <productname>kdbus</productname> ++ </refentryinfo> ++ ++ <refmeta> ++ <refentrytitle>kdbus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </refmeta> ++ ++ <refnamediv> ++ <refname>kdbus</refname> ++ <refpurpose>Kernel Message Bus</refpurpose> ++ </refnamediv> ++ ++ <refsect1> ++ <title>Synopsis</title> ++ <para> ++ kdbus is an inter-process communication bus system controlled by the ++ kernel. It provides user-space with an API to create buses and send ++ unicast and multicast messages to one, or many, peers connected to the ++ same bus. It does not enforce any layout on the transmitted data, but ++ only provides the transport layer used for message interchange between ++ peers. ++ </para> ++ <para> ++ This set of man-pages gives a comprehensive overview of the kernel-level ++ API, with all ioctl commands, associated structs and bit masks. However, ++ most people will not use this API level directly, but rather let one of ++ the high-level abstraction libraries help them integrate D-Bus ++ functionality into their applications. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>Description</title> ++ <para> ++ kdbus provides a pseudo filesystem called <emphasis>kdbusfs</emphasis>, ++ which is usually mounted on <filename>/sys/fs/kdbus</filename>. Bus ++ primitives can be accessed as files and sub-directories underneath this ++ mount-point. Any advanced operations are done via ++ <function>ioctl()</function> on files created by ++ <emphasis>kdbusfs</emphasis>. Multiple mount-points of ++ <emphasis>kdbusfs</emphasis> are independent of each other. This allows ++ namespacing of kdbus by mounting a new instance of ++ <emphasis>kdbusfs</emphasis> in a new mount-namespace. kdbus calls these ++ mount instances domains and each bus belongs to exactly one domain. ++ </para> ++ ++ <para> ++ kdbus was designed as a transport layer for D-Bus, but is in no way ++ limited, nor controlled by the D-Bus protocol specification. The D-Bus ++ protocol is one possible application layer on top of kdbus. ++ </para> ++ ++ <para> ++ For the general D-Bus protocol specification, its payload format, its ++ marshaling, and its communication semantics, please refer to the ++ <ulink url="http://dbus.freedesktop.org/doc/dbus-specification.html"> ++ D-Bus specification</ulink>. ++ </para> ++ ++ </refsect1> ++ ++ <refsect1> ++ <title>Terminology</title> ++ ++ <refsect2> ++ <title>Domain</title> ++ <para> ++ A domain is a <emphasis>kdbusfs</emphasis> mount-point containing all ++ the bus primitives. Each domain is independent, and separate domains ++ do not affect each other. ++ </para> ++ </refsect2> ++ ++ <refsect2> ++ <title>Bus</title> ++ <para> ++ 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 endpoint of ++ a bus, to which clients establish connections, is the "bus" file ++ /sys/fs/kdbus/<bus name>/bus. ++ Common operating system setups create one "system bus" per system, ++ and one "user bus" for every logged-in user. Applications or services ++ may create their own private buses. The kernel driver does not ++ distinguish between different bus types, they are all handled the same ++ way. See ++ <citerefentry> ++ <refentrytitle>kdbus.bus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more details. ++ </para> ++ </refsect2> ++ ++ <refsect2> ++ <title>Endpoint</title> ++ <para> ++ An endpoint provides a file to talk to a bus. Opening an endpoint ++ creates a new connection to the bus to which the endpoint belongs. All ++ endpoints have unique names and are accessible as files underneath the ++ directory of a bus, e.g., /sys/fs/kdbus/<bus>/<endpoint> ++ Every bus has a default endpoint called "bus". ++ A bus can optionally offer additional endpoints with custom names ++ to provide restricted access to the bus. Custom endpoints carry ++ additional policy which can be used to create sandboxes with ++ locked-down, limited, filtered access to a bus. See ++ <citerefentry> ++ <refentrytitle>kdbus.endpoint</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more details. ++ </para> ++ </refsect2> ++ ++ <refsect2> ++ <title>Connection</title> ++ <para> ++ A connection to a bus is created by opening an endpoint file of a ++ bus. Every ordinary 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 ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more details. ++ </para> ++ </refsect2> ++ ++ <refsect2> ++ <title>Pool</title> ++ <para> ++ Each connection allocates a piece of shmem-backed memory that is ++ used to receive messages and answers to ioctl commands from the kernel. ++ It is never used to send anything to the kernel. In order to access that ++ memory, an application must mmap() it into its address space. See ++ <citerefentry> ++ <refentrytitle>kdbus.pool</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more details. ++ </para> ++ </refsect2> ++ ++ <refsect2> ++ <title>Well-known Name</title> ++ <para> ++ 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. A ++ connection that offers a service on a bus is usually reached by its ++ well-known name. An analogy of connection ID and well-known name is an ++ IP address and a DNS name associated with that address. See ++ <citerefentry> ++ <refentrytitle>kdbus.name</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more details. ++ </para> ++ </refsect2> ++ ++ <refsect2> ++ <title>Message</title> ++ <para> ++ 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 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, just like passing file ++ descriptors over UNIX domain sockets. 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 time stamps, 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 ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more details. ++ </para> ++ </refsect2> ++ ++ <refsect2> ++ <title>Item</title> ++ <para> ++ The API of kdbus implements the notion of items, submitted through and ++ returned by most ioctls, and stored inside data structures in the ++ connection's pool. See ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more details. ++ </para> ++ </refsect2> ++ ++ <refsect2> ++ <title>Broadcast, signal, filter, match</title> ++ <para> ++ Signals are messages that a receiver opts in for by installing a blob of ++ bytes, called a 'match'. Signal messages must always carry a ++ counter-part blob, called a 'filter', and signals are only delivered to ++ peers which have a match that white-lists the message's filter. Senders ++ of signal messages can use either a single connection ID as receiver, ++ or the special connection ID ++ <constant>KDBUS_DST_ID_BROADCAST</constant> to potentially send it to ++ all connections of a bus, following the logic described above. See ++ <citerefentry> ++ <refentrytitle>kdbus.match</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ and ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more details. ++ </para> ++ </refsect2> ++ ++ <refsect2> ++ <title>Policy</title> ++ <para> ++ A policy is a set of rules that define which connections can see, talk ++ to, or register a well-known name on the bus. A policy is attached to ++ buses and custom endpoints, and modified by policy holder connections or ++ owners of custom endpoints. See ++ <citerefentry> ++ <refentrytitle>kdbus.policy</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more details. ++ </para> ++ </refsect2> ++ ++ <refsect2> ++ <title>Privileged bus users</title> ++ <para> ++ 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. See ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more details. ++ </para> ++ </refsect2> ++ </refsect1> ++ ++ <refsect1> ++ <title>Bus Layout</title> ++ ++ <para> ++ A <emphasis>bus</emphasis> provides and defines an environment that peers ++ can connect to for message interchange. A bus is created via the kdbus ++ control interface and can be modified by the bus creator. It applies the ++ policy that control all bus operations. The bus creator itself does not ++ participate as a peer. To establish a peer ++ <emphasis>connection</emphasis>, you have to open one of the ++ <emphasis>endpoints</emphasis> of a bus. Each bus provides a default ++ endpoint, but further endpoints can be created on-demand. Endpoints are ++ used to apply additional policies for all connections on this endpoint. ++ Thus, they provide additional filters to further restrict access of ++ specific connections to the bus. ++ </para> ++ ++ <para> ++ Following, you can see an example bus layout: ++ </para> ++ ++ <programlisting><![CDATA[ ++ Bus Creator ++ | ++ | ++ +-----+ ++ | Bus | ++ +-----+ ++ | ++ __________________/ \__________________ ++ / \ ++ | | ++ +----------+ +----------+ ++ | Endpoint | | Endpoint | ++ +----------+ +----------+ ++ _________/|\_________ _________/|\_________ ++ / | \ / | \ ++ | | | | | | ++ | | | | | | ++ Connection Connection Connection Connection Connection Connection ++ ]]></programlisting> ++ ++ </refsect1> ++ ++ <refsect1> ++ <title>Data structures and interconnections</title> ++ <programlisting><![CDATA[ ++ +--------------------------------------------------------------------------+ ++ | Domain (Mount Point) | ++ | /sys/fs/kdbus/control | ++ | +----------------------------------------------------------------------+ | ++ | | Bus (System Bus) | | ++ | | /sys/fs/kdbus/0-system/ | | ++ | | +-------------------------------+ +--------------------------------+ | | ++ | | | Endpoint | | Endpoint | | | ++ | | | /sys/fs/kdbus/0-system/bus | | /sys/fs/kdbus/0-system/ep.app | | | ++ | | +-------------------------------+ +--------------------------------+ | | ++ | | +--------------+ +--------------+ +--------------+ +---------------+ | | ++ | | | Connection | | Connection | | Connection | | Connection | | | ++ | | | :1.22 | | :1.25 | | :1.55 | | :1.81 | | | ++ | | +--------------+ +--------------+ +--------------+ +---------------+ | | ++ | +----------------------------------------------------------------------+ | ++ | | ++ | +----------------------------------------------------------------------+ | ++ | | Bus (User Bus for UID 2702) | | ++ | | /sys/fs/kdbus/2702-user/ | | ++ | | +-------------------------------+ +--------------------------------+ | | ++ | | | Endpoint | | Endpoint | | | ++ | | | /sys/fs/kdbus/2702-user/bus | | /sys/fs/kdbus/2702-user/ep.app | | | ++ | | +-------------------------------+ +--------------------------------+ | | ++ | | +--------------+ +--------------+ +--------------+ +---------------+ | | ++ | | | Connection | | Connection | | Connection | | Connection | | | ++ | | | :1.22 | | :1.25 | | :1.55 | | :1.81 | | | ++ | | +--------------+ +--------------+ +--------------------------------+ | | ++ | +----------------------------------------------------------------------+ | ++ +--------------------------------------------------------------------------+ ++ ]]></programlisting> ++ </refsect1> ++ ++ <refsect1> ++ <title>Metadata</title> ++ ++ <refsect2> ++ <title>When metadata is collected</title> ++ <para> ++ kdbus records data about the system in certain situations. Such metadata ++ can refer to the currently active process (creds, PIDs, current user ++ groups, process names and its executable path, cgroup membership, ++ capabilities, security label and audit information), connection ++ information (description string, currently owned names) and time stamps. ++ </para> ++ <para> ++ Metadata is collected at the following times. ++ </para> ++ ++ <itemizedlist> ++ <listitem><para> ++ When a bus is created (<constant>KDBUS_CMD_MAKE</constant>), ++ information about the calling task is collected. This data is returned ++ by the kernel via the <constant>KDBUS_CMD_BUS_CREATOR_INFO</constant> ++ call. ++ </para></listitem> ++ ++ <listitem> ++ <para> ++ When a connection is created (<constant>KDBUS_CMD_HELLO</constant>), ++ information about the calling task is collected. Alternatively, a ++ privileged connection may provide 'faked' information about ++ credentials, PIDs and security labels which will be stored instead. ++ This data is returned by the kernel as information on a connection ++ (<constant>KDBUS_CMD_CONN_INFO</constant>). Only metadata that a ++ connection allowed to be sent (by setting its bit in ++ <varname>attach_flags_send</varname>) will be exported in this way. ++ </para> ++ </listitem> ++ ++ <listitem> ++ <para> ++ When a message is sent (<constant>KDBUS_CMD_SEND</constant>), ++ information about the sending task and the sending connection are ++ collected. This metadata will be attached to the message when it ++ arrives in the receiver's pool. If the connection sending the ++ message installed faked credentials (see ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>), ++ the message will not be augmented by any information about the ++ currently sending task. Note that only metadata that was requested ++ by the receiving connection will be collected and attached to ++ messages. ++ </para> ++ </listitem> ++ </itemizedlist> ++ ++ <para> ++ Which metadata items are actually delivered depends on the following ++ sets and masks: ++ </para> ++ ++ <itemizedlist> ++ <listitem><para> ++ (a) the system-wide kmod creds mask ++ (module parameter <varname>attach_flags_mask</varname>) ++ </para></listitem> ++ ++ <listitem><para> ++ (b) the per-connection send creds mask, set by the connecting client ++ </para></listitem> ++ ++ <listitem><para> ++ (c) the per-connection receive creds mask, set by the connecting ++ client ++ </para></listitem> ++ ++ <listitem><para> ++ (d) the per-bus minimal creds mask, set by the bus creator ++ </para></listitem> ++ ++ <listitem><para> ++ (e) the per-bus owner creds mask, set by the bus creator ++ </para></listitem> ++ ++ <listitem><para> ++ (f) the mask specified when querying creds of a bus peer ++ </para></listitem> ++ ++ <listitem><para> ++ (g) the mask specified when querying creds of a bus owner ++ </para></listitem> ++ </itemizedlist> ++ ++ <para> ++ With the following rules: ++ </para> ++ ++ <itemizedlist> ++ <listitem> ++ <para> ++ [1] The creds attached to messages are determined as ++ <constant>a & b & c</constant>. ++ </para> ++ </listitem> ++ ++ <listitem> ++ <para> ++ [2] When connecting to a bus (<constant>KDBUS_CMD_HELLO</constant>), ++ and <constant>~b & d != 0</constant>, the call will fail with, ++ <errorcode>-1</errorcode>, and <varname>errno</varname> is set to ++ <constant>ECONNREFUSED</constant>. ++ </para> ++ </listitem> ++ ++ <listitem> ++ <para> ++ [3] When querying creds of a bus peer, the creds returned are ++ <constant>a & b & f</constant>. ++ </para> ++ </listitem> ++ ++ <listitem> ++ <para> ++ [4] When querying creds of a bus owner, the creds returned are ++ <constant>a & e & g</constant>. ++ </para> ++ </listitem> ++ </itemizedlist> ++ ++ <para> ++ Hence, programs might not always get all requested metadata items that ++ it requested. Code must be written so that it can cope with this fact. ++ </para> ++ </refsect2> ++ ++ <refsect2> ++ <title>Benefits and heads-up</title> ++ <para> ++ Attaching metadata to messages has two major benefits. ++ ++ <itemizedlist> ++ <listitem> ++ <para> ++ Metadata attached to messages is gathered at the moment when the ++ other side calls <constant>KDBUS_CMD_SEND</constant>, or, ++ respectively, then the kernel notification is generated. There is ++ no need for the receiving peer to retrieve information about the ++ task in a second step. This closes a race gap that would otherwise ++ be inherent. ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ As metadata is delivered along with messages in the same data ++ blob, no extra calls to kernel functions etc. are needed to gather ++ them. ++ </para> ++ </listitem> ++ </itemizedlist> ++ ++ Note, however, that collecting metadata does come at a price for ++ performance, so developers should carefully assess which metadata to ++ really opt-in for. For best practice, data that is not needed as part ++ of a message should not be requested by the connection in the first ++ place (see <varname>attach_flags_recv</varname> in ++ <constant>KDBUS_CMD_HELLO</constant>). ++ </para> ++ </refsect2> ++ ++ <refsect2> ++ <title>Attach flags for metadata items</title> ++ <para> ++ To let the kernel know which metadata information to attach as items ++ to the aforementioned commands, it uses a bitmask. In those, the ++ following <emphasis>attach flags</emphasis> are currently supported. ++ Both the the <varname>attach_flags_recv</varname> and ++ <varname>attach_flags_send</varname> fields of ++ <type>struct kdbus_cmd_hello</type>, as well as the payload of the ++ <constant>KDBUS_ITEM_ATTACH_FLAGS_SEND</constant> and ++ <constant>KDBUS_ITEM_ATTACH_FLAGS_RECV</constant> items follow this ++ scheme. ++ </para> ++ ++ <variablelist> ++ <varlistentry> ++ <term><constant>KDBUS_ATTACH_TIMESTAMP</constant></term> ++ <listitem><para> ++ Requests the attachment of an item of type ++ <constant>KDBUS_ITEM_TIMESTAMP</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ATTACH_CREDS</constant></term> ++ <listitem><para> ++ Requests the attachment of an item of type ++ <constant>KDBUS_ITEM_CREDS</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ATTACH_PIDS</constant></term> ++ <listitem><para> ++ Requests the attachment of an item of type ++ <constant>KDBUS_ITEM_PIDS</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ATTACH_AUXGROUPS</constant></term> ++ <listitem><para> ++ Requests the attachment of an item of type ++ <constant>KDBUS_ITEM_AUXGROUPS</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ATTACH_NAMES</constant></term> ++ <listitem><para> ++ Requests the attachment of an item of type ++ <constant>KDBUS_ITEM_OWNED_NAME</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ATTACH_TID_COMM</constant></term> ++ <listitem><para> ++ Requests the attachment of an item of type ++ <constant>KDBUS_ITEM_TID_COMM</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ATTACH_PID_COMM</constant></term> ++ <listitem><para> ++ Requests the attachment of an item of type ++ <constant>KDBUS_ITEM_PID_COMM</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ATTACH_EXE</constant></term> ++ <listitem><para> ++ Requests the attachment of an item of type ++ <constant>KDBUS_ITEM_EXE</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ATTACH_CMDLINE</constant></term> ++ <listitem><para> ++ Requests the attachment of an item of type ++ <constant>KDBUS_ITEM_CMDLINE</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ATTACH_CGROUP</constant></term> ++ <listitem><para> ++ Requests the attachment of an item of type ++ <constant>KDBUS_ITEM_CGROUP</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ATTACH_CAPS</constant></term> ++ <listitem><para> ++ Requests the attachment of an item of type ++ <constant>KDBUS_ITEM_CAPS</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ATTACH_SECLABEL</constant></term> ++ <listitem><para> ++ Requests the attachment of an item of type ++ <constant>KDBUS_ITEM_SECLABEL</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ATTACH_AUDIT</constant></term> ++ <listitem><para> ++ Requests the attachment of an item of type ++ <constant>KDBUS_ITEM_AUDIT</constant>. ++ </para></listitem> ++ </varlistentry> ++ ++ <varlistentry> ++ <term><constant>KDBUS_ATTACH_CONN_DESCRIPTION</constant></term> ++ <listitem><para> ++ Requests the attachment of an item of type ++ <constant>KDBUS_ITEM_CONN_DESCRIPTION</constant>. ++ </para></listitem> ++ </varlistentry> ++ </variablelist> ++ ++ <para> ++ Please refer to ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for detailed information about the layout and payload of items and ++ what metadata should be used to. ++ </para> ++ </refsect2> ++ </refsect1> ++ ++ <refsect1> ++ <title>The ioctl interface</title> ++ ++ <para> ++ As stated in the 'synopsis' section above, application developers are ++ strongly encouraged to use kdbus through one of the high-level D-Bus ++ abstraction libraries, rather than using the low-level API directly. ++ </para> ++ ++ <para> ++ kdbus on the kernel level exposes its functions exclusively through ++ <citerefentry> ++ <refentrytitle>ioctl</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry>, ++ employed on file descriptors returned by ++ <citerefentry> ++ <refentrytitle>open</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry> ++ on pseudo files exposed by ++ <citerefentry> ++ <refentrytitle>kdbus.fs</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>. ++ </para> ++ <para> ++ Following is a list of all the ioctls, along with the command structs ++ they must be used with. ++ </para> ++ ++ <informaltable frame="none"> ++ <tgroup cols="3" colsep="1"> ++ <thead> ++ <row> ++ <entry>ioctl signature</entry> ++ <entry>command</entry> ++ <entry>transported struct</entry> ++ </row> ++ </thead> ++ <tbody> ++ <row> ++ <entry><constant>0x40189500</constant></entry> ++ <entry><constant>KDBUS_CMD_BUS_MAKE</constant></entry> ++ <entry><type>struct kdbus_cmd *</type></entry> ++ </row><row> ++ <entry><constant>0x40189510</constant></entry> ++ <entry><constant>KDBUS_CMD_ENDPOINT_MAKE</constant></entry> ++ <entry><type>struct kdbus_cmd *</type></entry> ++ </row><row> ++ <entry><constant>0xc0609580</constant></entry> ++ <entry><constant>KDBUS_CMD_HELLO</constant></entry> ++ <entry><type>struct kdbus_cmd_hello *</type></entry> ++ </row><row> ++ <entry><constant>0x40189582</constant></entry> ++ <entry><constant>KDBUS_CMD_BYEBYE</constant></entry> ++ <entry><type>struct kdbus_cmd *</type></entry> ++ </row><row> ++ <entry><constant>0x40389590</constant></entry> ++ <entry><constant>KDBUS_CMD_SEND</constant></entry> ++ <entry><type>struct kdbus_cmd_send *</type></entry> ++ </row><row> ++ <entry><constant>0x80409591</constant></entry> ++ <entry><constant>KDBUS_CMD_RECV</constant></entry> ++ <entry><type>struct kdbus_cmd_recv *</type></entry> ++ </row><row> ++ <entry><constant>0x40209583</constant></entry> ++ <entry><constant>KDBUS_CMD_FREE</constant></entry> ++ <entry><type>struct kdbus_cmd_free *</type></entry> ++ </row><row> ++ <entry><constant>0x401895a0</constant></entry> ++ <entry><constant>KDBUS_CMD_NAME_ACQUIRE</constant></entry> ++ <entry><type>struct kdbus_cmd *</type></entry> ++ </row><row> ++ <entry><constant>0x401895a1</constant></entry> ++ <entry><constant>KDBUS_CMD_NAME_RELEASE</constant></entry> ++ <entry><type>struct kdbus_cmd *</type></entry> ++ </row><row> ++ <entry><constant>0x80289586</constant></entry> ++ <entry><constant>KDBUS_CMD_LIST</constant></entry> ++ <entry><type>struct kdbus_cmd_list *</type></entry> ++ </row><row> ++ <entry><constant>0x80309584</constant></entry> ++ <entry><constant>KDBUS_CMD_CONN_INFO</constant></entry> ++ <entry><type>struct kdbus_cmd_info *</type></entry> ++ </row><row> ++ <entry><constant>0x40209551</constant></entry> ++ <entry><constant>KDBUS_CMD_UPDATE</constant></entry> ++ <entry><type>struct kdbus_cmd *</type></entry> ++ </row><row> ++ <entry><constant>0x80309585</constant></entry> ++ <entry><constant>KDBUS_CMD_BUS_CREATOR_INFO</constant></entry> ++ <entry><type>struct kdbus_cmd_info *</type></entry> ++ </row><row> ++ <entry><constant>0x40189511</constant></entry> ++ <entry><constant>KDBUS_CMD_ENDPOINT_UPDATE</constant></entry> ++ <entry><type>struct kdbus_cmd *</type></entry> ++ </row><row> ++ <entry><constant>0x402095b0</constant></entry> ++ <entry><constant>KDBUS_CMD_MATCH_ADD</constant></entry> ++ <entry><type>struct kdbus_cmd_match *</type></entry> ++ </row><row> ++ <entry><constant>0x402095b1</constant></entry> ++ <entry><constant>KDBUS_CMD_MATCH_REMOVE</constant></entry> ++ <entry><type>struct kdbus_cmd_match *</type></entry> ++ </row> ++ </tbody> ++ </tgroup> ++ </informaltable> ++ ++ <para> ++ Depending on the type of <emphasis>kdbusfs</emphasis> node that was ++ opened and what ioctls have been executed on a file descriptor before, ++ a different sub-set of ioctl commands is allowed. ++ </para> ++ ++ <itemizedlist> ++ <listitem> ++ <para> ++ On a file descriptor resulting from opening a ++ <emphasis>control node</emphasis>, only the ++ <constant>KDBUS_CMD_BUS_MAKE</constant> ioctl may be executed. ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ On a file descriptor resulting from opening a ++ <emphasis>bus endpoint node</emphasis>, only the ++ <constant>KDBUS_CMD_ENDPOINT_MAKE</constant> and ++ <constant>KDBUS_CMD_HELLO</constant> ioctls may be executed. ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ A file descriptor that was used to create a bus ++ (via <constant>KDBUS_CMD_BUS_MAKE</constant>) is called a ++ <emphasis>bus owner</emphasis> file descriptor. The bus will be ++ active as long as the file descriptor is kept open. ++ A bus owner file descriptor can not be used to ++ employ any further ioctls. As soon as ++ <citerefentry> ++ <refentrytitle>close</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry> ++ is called on it, the bus will be shut down, along will all associated ++ endpoints and connections. See ++ <citerefentry> ++ <refentrytitle>kdbus.bus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more details. ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ A file descriptor that was used to create an endpoint ++ (via <constant>KDBUS_CMD_ENDPOINT_MAKE</constant>) is called an ++ <emphasis>endpoint owner</emphasis> file descriptor. The endpoint ++ will be active as long as the file descriptor is kept open. ++ An endpoint owner file descriptor can only be used ++ to update details of an endpoint through the ++ <constant>KDBUS_CMD_ENDPOINT_UPDATE</constant> ioctl. As soon as ++ <citerefentry> ++ <refentrytitle>close</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry> ++ is called on it, the endpoint will be removed from the bus, and all ++ connections that are connected to the bus through it are shut down. ++ See ++ <citerefentry> ++ <refentrytitle>kdbus.endpoint</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ for more details. ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ A file descriptor that was used to create a connection ++ (via <constant>KDBUS_CMD_HELLO</constant>) is called a ++ <emphasis>connection owner</emphasis> file descriptor. The connection ++ will be active as long as the file descriptor is kept open. ++ A connection owner file descriptor may be used to ++ issue any of the following ioctls. ++ </para> ++ ++ <itemizedlist> ++ <listitem><para> ++ <constant>KDBUS_CMD_UPDATE</constant> to tweak details of the ++ connection. See ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>. ++ </para></listitem> ++ ++ <listitem><para> ++ <constant>KDBUS_CMD_BYEBYE</constant> to shut down a connection ++ without losing messages. See ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>. ++ </para></listitem> ++ ++ <listitem><para> ++ <constant>KDBUS_CMD_FREE</constant> to free a slice of memory in ++ the pool. See ++ <citerefentry> ++ <refentrytitle>kdbus.pool</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>. ++ </para></listitem> ++ ++ <listitem><para> ++ <constant>KDBUS_CMD_CONN_INFO</constant> to retrieve information ++ on other connections on the bus. See ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>. ++ </para></listitem> ++ ++ <listitem><para> ++ <constant>KDBUS_CMD_BUS_CREATOR_INFO</constant> to retrieve ++ information on the bus creator. See ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>. ++ </para></listitem> ++ ++ <listitem><para> ++ <constant>KDBUS_CMD_LIST</constant> to retrieve a list of ++ currently active well-known names and unique IDs on the bus. See ++ <citerefentry> ++ <refentrytitle>kdbus.name</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>. ++ </para></listitem> ++ ++ <listitem><para> ++ <constant>KDBUS_CMD_SEND</constant> and ++ <constant>KDBUS_CMD_RECV</constant> to send or receive a message. ++ See ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>. ++ </para></listitem> ++ ++ <listitem><para> ++ <constant>KDBUS_CMD_NAME_ACQUIRE</constant> and ++ <constant>KDBUS_CMD_NAME_RELEASE</constant> to acquire or release ++ a well-known name on the bus. See ++ <citerefentry> ++ <refentrytitle>kdbus.name</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>. ++ </para></listitem> ++ ++ <listitem><para> ++ <constant>KDBUS_CMD_MATCH_ADD</constant> and ++ <constant>KDBUS_CMD_MATCH_REMOVE</constant> to add or remove ++ a match for signal messages. See ++ <citerefentry> ++ <refentrytitle>kdbus.match</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry>. ++ </para></listitem> ++ </itemizedlist> ++ </listitem> ++ </itemizedlist> ++ ++ <para> ++ These ioctls, along with the structs they transport, are explained in ++ detail in the other documents linked to in the 'see also' section below. ++ </para> ++ </refsect1> ++ ++ <refsect1> ++ <title>See Also</title> ++ <simplelist type="inline"> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.bus</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.connection</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.endpoint</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.fs</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.item</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.message</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.name</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>kdbus.pool</refentrytitle> ++ <manvolnum>7</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>ioctl</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>mmap</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>open</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <citerefentry> ++ <refentrytitle>close</refentrytitle> ++ <manvolnum>2</manvolnum> ++ </citerefentry> ++ </member> ++ <member> ++ <ulink url="http://freedesktop.org/wiki/Software/dbus">D-Bus</ulink> ++ </member> ++ </simplelist> ++ </refsect1> ++ ++</refentry> +diff --git a/Documentation/kdbus/stylesheet.xsl b/Documentation/kdbus/stylesheet.xsl +new file mode 100644 +index 000000000000..52565eac7d0d +--- /dev/null ++++ b/Documentation/kdbus/stylesheet.xsl +@@ -0,0 +1,16 @@ ++<?xml version="1.0" encoding="UTF-8"?> ++<stylesheet xmlns="http://www.w3.org/1999/XSL/Transform" version="1.0"> ++ <param name="chunk.quietly">1</param> ++ <param name="funcsynopsis.style">ansi</param> ++ <param name="funcsynopsis.tabular.threshold">80</param> ++ <param name="callout.graphics">0</param> ++ <param name="paper.type">A4</param> ++ <param name="generate.section.toc.level">2</param> ++ <param name="use.id.as.filename">1</param> ++ <param name="citerefentry.link">1</param> ++ <strip-space elements="*"/> ++ <template name="generate.citerefentry.link"> ++ <value-of select="refentrytitle"/> ++ <text>.html</text> ++ </template> ++</stylesheet> +diff --git a/Makefile b/Makefile +index 4ce793e576cf..1df89975465f 100644 +--- a/Makefile ++++ b/Makefile +@@ -1344,6 +1344,7 @@ $(help-board-dirs): help-%: + %docs: scripts_basic FORCE + $(Q)$(MAKE) $(build)=scripts build_docproc + $(Q)$(MAKE) $(build)=Documentation/DocBook $@ ++ $(Q)$(MAKE) $(build)=Documentation/kdbus $@ + + else # KBUILD_EXTMOD + diff --git a/kdbus-add-driver-skeleton-ioctl-entry-points-and-uti.patch b/kdbus-add-driver-skeleton-ioctl-entry-points-and-uti.patch new file mode 100644 index 000000000..ed9291d57 --- /dev/null +++ b/kdbus-add-driver-skeleton-ioctl-entry-points-and-uti.patch @@ -0,0 +1,1253 @@ +From: Daniel Mack <daniel@zonque.org> +Date: Thu, 11 Sep 2014 18:52:52 +0200 +Subject: [PATCH] kdbus: add driver skeleton, ioctl entry points and utility + functions + +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: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Djalal Harouni <tixxdz@opendz.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + Documentation/ioctl/ioctl-number.txt | 1 + + ipc/kdbus/handle.c | 617 +++++++++++++++++++++++++++++++++++ + ipc/kdbus/handle.h | 85 +++++ + ipc/kdbus/limits.h | 64 ++++ + ipc/kdbus/main.c | 125 +++++++ + ipc/kdbus/util.c | 201 ++++++++++++ + ipc/kdbus/util.h | 74 +++++ + 7 files changed, 1167 insertions(+) + create mode 100644 ipc/kdbus/handle.c + create mode 100644 ipc/kdbus/handle.h + create mode 100644 ipc/kdbus/limits.h + create mode 100644 ipc/kdbus/main.c + create mode 100644 ipc/kdbus/util.c + create mode 100644 ipc/kdbus/util.h + +diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt +index 611c52267d24..1e166ad3e1d7 100644 +--- 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> +diff --git a/ipc/kdbus/handle.c b/ipc/kdbus/handle.c +new file mode 100644 +index 000000000000..f72dbe513b4a +--- /dev/null ++++ b/ipc/kdbus/handle.c +@@ -0,0 +1,617 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> ++ * ++ * 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/file.h> ++#include <linux/fs.h> ++#include <linux/idr.h> ++#include <linux/init.h> ++#include <linux/kdev_t.h> ++#include <linux/module.h> ++#include <linux/poll.h> ++#include <linux/rwsem.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 "fs.h" ++#include "handle.h" ++#include "item.h" ++#include "match.h" ++#include "message.h" ++#include "names.h" ++#include "domain.h" ++#include "policy.h" ++ ++static int kdbus_args_verify(struct kdbus_args *args) ++{ ++ struct kdbus_item *item; ++ size_t i; ++ int ret; ++ ++ KDBUS_ITEMS_FOREACH(item, args->items, args->items_size) { ++ struct kdbus_arg *arg = NULL; ++ ++ if (!KDBUS_ITEM_VALID(item, args->items, args->items_size)) ++ return -EINVAL; ++ ++ for (i = 0; i < args->argc; ++i) ++ if (args->argv[i].type == item->type) ++ break; ++ if (i >= args->argc) ++ return -EINVAL; ++ ++ arg = &args->argv[i]; ++ ++ ret = kdbus_item_validate(item); ++ if (ret < 0) ++ return ret; ++ ++ if (arg->item && !arg->multiple) ++ return -EINVAL; ++ ++ arg->item = item; ++ } ++ ++ if (!KDBUS_ITEMS_END(item, args->items, args->items_size)) ++ return -EINVAL; ++ ++ for (i = 0; i < args->argc; ++i) ++ if (args->argv[i].mandatory && !args->argv[i].item) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int kdbus_args_negotiate(struct kdbus_args *args) ++{ ++ struct kdbus_item __user *user; ++ struct kdbus_item *negotiation; ++ size_t i, j, num; ++ ++ /* ++ * If KDBUS_FLAG_NEGOTIATE is set, we overwrite the flags field with ++ * the set of supported flags. Furthermore, if an KDBUS_ITEM_NEGOTIATE ++ * item is passed, we iterate its payload (array of u64, each set to an ++ * item type) and clear all unsupported item-types to 0. ++ * The caller might do this recursively, if other flags or objects are ++ * embedded in the payload itself. ++ */ ++ ++ if (args->cmd->flags & KDBUS_FLAG_NEGOTIATE) { ++ if (put_user(args->allowed_flags & ~KDBUS_FLAG_NEGOTIATE, ++ &args->user->flags)) ++ return -EFAULT; ++ } ++ ++ if (args->argc < 1 || args->argv[0].type != KDBUS_ITEM_NEGOTIATE || ++ !args->argv[0].item) ++ return 0; ++ ++ negotiation = args->argv[0].item; ++ user = (struct kdbus_item __user *) ++ ((u8 __user *)args->user + ++ ((u8 *)negotiation - (u8 *)args->cmd)); ++ num = KDBUS_ITEM_PAYLOAD_SIZE(negotiation) / sizeof(u64); ++ ++ for (i = 0; i < num; ++i) { ++ for (j = 0; j < args->argc; ++j) ++ if (negotiation->data64[i] == args->argv[j].type) ++ break; ++ ++ if (j < args->argc) ++ continue; ++ ++ /* this item is not supported, clear it out */ ++ negotiation->data64[i] = 0; ++ if (put_user(negotiation->data64[i], &user->data64[i])) ++ return -EFAULT; ++ } ++ ++ return 0; ++} ++ ++/** ++ * __kdbus_args_parse() - parse payload of kdbus command ++ * @args: object to parse data into ++ * @argp: user-space location of command payload to parse ++ * @type_size: overall size of command payload to parse ++ * @items_offset: offset of items array in command payload ++ * @out: output variable to store pointer to copied payload ++ * ++ * This parses the ioctl payload at user-space location @argp into @args. @args ++ * must be pre-initialized by the caller to reflect the supported flags and ++ * items of this command. This parser will then copy the command payload into ++ * kernel-space, verify correctness and consistency and cache pointers to parsed ++ * items and other data in @args. ++ * ++ * If this function succeeded, you must call kdbus_args_clear() to release ++ * allocated resources before destroying @args. ++ * ++ * Return: On failure a negative error code is returned. Otherwise, 1 is ++ * returned if negotiation was requested, 0 if not. ++ */ ++int __kdbus_args_parse(struct kdbus_args *args, void __user *argp, ++ size_t type_size, size_t items_offset, void **out) ++{ ++ int ret; ++ ++ args->cmd = kdbus_memdup_user(argp, type_size, KDBUS_CMD_MAX_SIZE); ++ if (IS_ERR(args->cmd)) ++ return PTR_ERR(args->cmd); ++ ++ args->cmd->return_flags = 0; ++ args->user = argp; ++ args->items = (void *)((u8 *)args->cmd + items_offset); ++ args->items_size = args->cmd->size - items_offset; ++ ++ if (args->cmd->flags & ~args->allowed_flags) { ++ ret = -EINVAL; ++ goto error; ++ } ++ ++ ret = kdbus_args_verify(args); ++ if (ret < 0) ++ goto error; ++ ++ ret = kdbus_args_negotiate(args); ++ if (ret < 0) ++ goto error; ++ ++ *out = args->cmd; ++ return !!(args->cmd->flags & KDBUS_FLAG_NEGOTIATE); ++ ++error: ++ return kdbus_args_clear(args, ret); ++} ++ ++/** ++ * kdbus_args_clear() - release allocated command resources ++ * @args: object to release resources of ++ * @ret: return value of this command ++ * ++ * This frees all allocated resources on @args and copies the command result ++ * flags into user-space. @ret is usually returned unchanged by this function, ++ * so it can be used in the final 'return' statement of the command handler. ++ * ++ * Return: -EFAULT if return values cannot be copied into user-space, otherwise ++ * @ret is returned unchanged. ++ */ ++int kdbus_args_clear(struct kdbus_args *args, int ret) ++{ ++ if (!args) ++ return ret; ++ ++ if (!IS_ERR_OR_NULL(args->cmd)) { ++ if (put_user(args->cmd->return_flags, ++ &args->user->return_flags)) ++ ret = -EFAULT; ++ kfree(args->cmd); ++ args->cmd = NULL; ++ } ++ ++ return ret; ++} ++ ++/** ++ * enum kdbus_handle_type - type an handle can be of ++ * @KDBUS_HANDLE_NONE: no type set, yet ++ * @KDBUS_HANDLE_BUS_OWNER: bus owner ++ * @KDBUS_HANDLE_EP_OWNER: endpoint owner ++ * @KDBUS_HANDLE_CONNECTED: endpoint connection after HELLO ++ */ ++enum kdbus_handle_type { ++ KDBUS_HANDLE_NONE, ++ KDBUS_HANDLE_BUS_OWNER, ++ KDBUS_HANDLE_EP_OWNER, ++ KDBUS_HANDLE_CONNECTED, ++}; ++ ++/** ++ * struct kdbus_handle - handle to the kdbus system ++ * @rwlock: handle lock ++ * @type: type of this handle (KDBUS_HANDLE_*) ++ * @bus_owner: bus this handle owns ++ * @ep_owner: endpoint this handle owns ++ * @conn: connection this handle owns ++ * @privileged: Flag to mark a handle as privileged ++ */ ++struct kdbus_handle { ++ struct rw_semaphore rwlock; ++ ++ enum kdbus_handle_type type; ++ union { ++ struct kdbus_bus *bus_owner; ++ struct kdbus_ep *ep_owner; ++ struct kdbus_conn *conn; ++ }; ++ ++ bool privileged:1; ++}; ++ ++static int kdbus_handle_open(struct inode *inode, struct file *file) ++{ ++ struct kdbus_handle *handle; ++ struct kdbus_node *node; ++ int ret; ++ ++ node = kdbus_node_from_inode(inode); ++ if (!kdbus_node_acquire(node)) ++ return -ESHUTDOWN; ++ ++ handle = kzalloc(sizeof(*handle), GFP_KERNEL); ++ if (!handle) { ++ ret = -ENOMEM; ++ goto exit; ++ } ++ ++ init_rwsem(&handle->rwlock); ++ handle->type = KDBUS_HANDLE_NONE; ++ ++ if (node->type == KDBUS_NODE_ENDPOINT) { ++ struct kdbus_ep *ep = kdbus_ep_from_node(node); ++ struct kdbus_bus *bus = ep->bus; ++ ++ /* ++ * A connection is privileged if it is opened on an endpoint ++ * without custom policy and either: ++ * * the user has CAP_IPC_OWNER in the domain user namespace ++ * or ++ * * the callers euid matches the uid of the bus creator ++ */ ++ if (!ep->user && ++ (ns_capable(bus->domain->user_namespace, CAP_IPC_OWNER) || ++ uid_eq(file->f_cred->euid, bus->node.uid))) ++ handle->privileged = true; ++ } ++ ++ file->private_data = handle; ++ ret = 0; ++ ++exit: ++ kdbus_node_release(node); ++ 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_BUS_OWNER: ++ if (handle->bus_owner) { ++ kdbus_node_deactivate(&handle->bus_owner->node); ++ kdbus_bus_unref(handle->bus_owner); ++ } ++ break; ++ case KDBUS_HANDLE_EP_OWNER: ++ if (handle->ep_owner) { ++ kdbus_node_deactivate(&handle->ep_owner->node); ++ kdbus_ep_unref(handle->ep_owner); ++ } ++ break; ++ case KDBUS_HANDLE_CONNECTED: ++ kdbus_conn_disconnect(handle->conn, false); ++ kdbus_conn_unref(handle->conn); ++ break; ++ case KDBUS_HANDLE_NONE: ++ /* nothing to clean up */ ++ break; ++ } ++ ++ kfree(handle); ++ ++ return 0; ++} ++ ++static long kdbus_handle_ioctl_control(struct file *file, unsigned int cmd, ++ void __user *argp) ++{ ++ struct kdbus_handle *handle = file->private_data; ++ struct kdbus_node *node = file_inode(file)->i_private; ++ struct kdbus_domain *domain; ++ int ret = 0; ++ ++ if (!kdbus_node_acquire(node)) ++ return -ESHUTDOWN; ++ ++ /* ++ * The parent of control-nodes is always a domain, make sure to pin it ++ * so the parent is actually valid. ++ */ ++ domain = kdbus_domain_from_node(node->parent); ++ if (!kdbus_node_acquire(&domain->node)) { ++ kdbus_node_release(node); ++ return -ESHUTDOWN; ++ } ++ ++ switch (cmd) { ++ case KDBUS_CMD_BUS_MAKE: { ++ struct kdbus_bus *bus; ++ ++ bus = kdbus_cmd_bus_make(domain, argp); ++ if (IS_ERR_OR_NULL(bus)) { ++ ret = PTR_ERR_OR_ZERO(bus); ++ break; ++ } ++ ++ handle->type = KDBUS_HANDLE_BUS_OWNER; ++ handle->bus_owner = bus; ++ break; ++ } ++ ++ default: ++ ret = -EBADFD; ++ break; ++ } ++ ++ kdbus_node_release(&domain->node); ++ kdbus_node_release(node); ++ return ret; ++} ++ ++static long kdbus_handle_ioctl_ep(struct file *file, unsigned int cmd, ++ void __user *buf) ++{ ++ struct kdbus_handle *handle = file->private_data; ++ struct kdbus_node *node = file_inode(file)->i_private; ++ struct kdbus_ep *ep, *file_ep = kdbus_ep_from_node(node); ++ struct kdbus_conn *conn; ++ int ret = 0; ++ ++ if (!kdbus_node_acquire(node)) ++ return -ESHUTDOWN; ++ ++ switch (cmd) { ++ case KDBUS_CMD_ENDPOINT_MAKE: ++ /* creating custom endpoints is a privileged operation */ ++ if (!handle->privileged) { ++ ret = -EPERM; ++ break; ++ } ++ ++ ep = kdbus_cmd_ep_make(file_ep->bus, buf); ++ if (IS_ERR_OR_NULL(ep)) { ++ ret = PTR_ERR_OR_ZERO(ep); ++ break; ++ } ++ ++ handle->type = KDBUS_HANDLE_EP_OWNER; ++ handle->ep_owner = ep; ++ break; ++ ++ case KDBUS_CMD_HELLO: ++ conn = kdbus_cmd_hello(file_ep, handle->privileged, buf); ++ if (IS_ERR_OR_NULL(conn)) { ++ ret = PTR_ERR_OR_ZERO(conn); ++ break; ++ } ++ ++ handle->type = KDBUS_HANDLE_CONNECTED; ++ handle->conn = conn; ++ break; ++ ++ default: ++ ret = -EBADFD; ++ break; ++ } ++ ++ kdbus_node_release(node); ++ return ret; ++} ++ ++static long kdbus_handle_ioctl_ep_owner(struct file *file, unsigned int command, ++ void __user *buf) ++{ ++ struct kdbus_handle *handle = file->private_data; ++ struct kdbus_ep *ep = handle->ep_owner; ++ int ret; ++ ++ if (!kdbus_node_acquire(&ep->node)) ++ return -ESHUTDOWN; ++ ++ switch (command) { ++ case KDBUS_CMD_ENDPOINT_UPDATE: ++ ret = kdbus_cmd_ep_update(ep, buf); ++ break; ++ default: ++ ret = -EBADFD; ++ break; ++ } ++ ++ kdbus_node_release(&ep->node); ++ return ret; ++} ++ ++static long kdbus_handle_ioctl_connected(struct file *file, ++ unsigned int command, void __user *buf) ++{ ++ struct kdbus_handle *handle = file->private_data; ++ struct kdbus_conn *conn = handle->conn; ++ struct kdbus_conn *release_conn = NULL; ++ int ret; ++ ++ release_conn = conn; ++ ret = kdbus_conn_acquire(release_conn); ++ if (ret < 0) ++ return ret; ++ ++ switch (command) { ++ case KDBUS_CMD_BYEBYE: ++ /* ++ * 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. ++ */ ++ kdbus_conn_release(release_conn); ++ release_conn = NULL; ++ ret = kdbus_cmd_byebye_unlocked(conn, buf); ++ break; ++ case KDBUS_CMD_NAME_ACQUIRE: ++ ret = kdbus_cmd_name_acquire(conn, buf); ++ break; ++ case KDBUS_CMD_NAME_RELEASE: ++ ret = kdbus_cmd_name_release(conn, buf); ++ break; ++ case KDBUS_CMD_LIST: ++ ret = kdbus_cmd_list(conn, buf); ++ break; ++ case KDBUS_CMD_CONN_INFO: ++ ret = kdbus_cmd_conn_info(conn, buf); ++ break; ++ case KDBUS_CMD_BUS_CREATOR_INFO: ++ ret = kdbus_cmd_bus_creator_info(conn, buf); ++ break; ++ case KDBUS_CMD_UPDATE: ++ ret = kdbus_cmd_update(conn, buf); ++ break; ++ case KDBUS_CMD_MATCH_ADD: ++ ret = kdbus_cmd_match_add(conn, buf); ++ break; ++ case KDBUS_CMD_MATCH_REMOVE: ++ ret = kdbus_cmd_match_remove(conn, buf); ++ break; ++ case KDBUS_CMD_SEND: ++ ret = kdbus_cmd_send(conn, file, buf); ++ break; ++ case KDBUS_CMD_RECV: ++ ret = kdbus_cmd_recv(conn, buf); ++ break; ++ case KDBUS_CMD_FREE: ++ ret = kdbus_cmd_free(conn, buf); ++ break; ++ default: ++ ret = -EBADFD; ++ break; ++ } ++ ++ kdbus_conn_release(release_conn); ++ return ret; ++} ++ ++static long kdbus_handle_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ struct kdbus_handle *handle = file->private_data; ++ struct kdbus_node *node = kdbus_node_from_inode(file_inode(file)); ++ void __user *argp = (void __user *)arg; ++ long ret = -EBADFD; ++ ++ switch (cmd) { ++ case KDBUS_CMD_BUS_MAKE: ++ case KDBUS_CMD_ENDPOINT_MAKE: ++ case KDBUS_CMD_HELLO: ++ /* bail out early if already typed */ ++ if (handle->type != KDBUS_HANDLE_NONE) ++ break; ++ ++ down_write(&handle->rwlock); ++ if (handle->type == KDBUS_HANDLE_NONE) { ++ if (node->type == KDBUS_NODE_CONTROL) ++ ret = kdbus_handle_ioctl_control(file, cmd, ++ argp); ++ else if (node->type == KDBUS_NODE_ENDPOINT) ++ ret = kdbus_handle_ioctl_ep(file, cmd, argp); ++ } ++ up_write(&handle->rwlock); ++ break; ++ ++ case KDBUS_CMD_ENDPOINT_UPDATE: ++ case KDBUS_CMD_BYEBYE: ++ case KDBUS_CMD_NAME_ACQUIRE: ++ case KDBUS_CMD_NAME_RELEASE: ++ case KDBUS_CMD_LIST: ++ case KDBUS_CMD_CONN_INFO: ++ case KDBUS_CMD_BUS_CREATOR_INFO: ++ case KDBUS_CMD_UPDATE: ++ case KDBUS_CMD_MATCH_ADD: ++ case KDBUS_CMD_MATCH_REMOVE: ++ case KDBUS_CMD_SEND: ++ case KDBUS_CMD_RECV: ++ case KDBUS_CMD_FREE: ++ down_read(&handle->rwlock); ++ if (handle->type == KDBUS_HANDLE_EP_OWNER) ++ ret = kdbus_handle_ioctl_ep_owner(file, cmd, argp); ++ else if (handle->type == KDBUS_HANDLE_CONNECTED) ++ ret = kdbus_handle_ioctl_connected(file, cmd, argp); ++ up_read(&handle->rwlock); ++ break; ++ default: ++ ret = -ENOTTY; ++ break; ++ } ++ ++ return ret < 0 ? ret : 0; ++} ++ ++static unsigned int kdbus_handle_poll(struct file *file, ++ struct poll_table_struct *wait) ++{ ++ struct kdbus_handle *handle = file->private_data; ++ unsigned int mask = POLLOUT | POLLWRNORM; ++ int ret; ++ ++ /* Only a connected endpoint can read/write data */ ++ down_read(&handle->rwlock); ++ if (handle->type != KDBUS_HANDLE_CONNECTED) { ++ up_read(&handle->rwlock); ++ return POLLERR | POLLHUP; ++ } ++ up_read(&handle->rwlock); ++ ++ ret = kdbus_conn_acquire(handle->conn); ++ if (ret < 0) ++ return POLLERR | POLLHUP; ++ ++ poll_wait(file, &handle->conn->wait, wait); ++ ++ if (!list_empty(&handle->conn->queue.msg_list) || ++ atomic_read(&handle->conn->lost_count) > 0) ++ mask |= POLLIN | POLLRDNORM; ++ ++ kdbus_conn_release(handle->conn); ++ ++ return mask; ++} ++ ++static int kdbus_handle_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct kdbus_handle *handle = file->private_data; ++ int ret = -EBADFD; ++ ++ if (down_read_trylock(&handle->rwlock)) { ++ if (handle->type == KDBUS_HANDLE_CONNECTED) ++ ret = kdbus_pool_mmap(handle->conn->pool, vma); ++ up_read(&handle->rwlock); ++ } ++ return ret; ++} ++ ++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 ++}; +diff --git a/ipc/kdbus/handle.h b/ipc/kdbus/handle.h +new file mode 100644 +index 000000000000..93a372d554a2 +--- /dev/null ++++ b/ipc/kdbus/handle.h +@@ -0,0 +1,85 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#ifndef __KDBUS_HANDLE_H ++#define __KDBUS_HANDLE_H ++ ++#include <linux/fs.h> ++#include <uapi/linux/kdbus.h> ++ ++extern const struct file_operations kdbus_handle_ops; ++ ++/** ++ * kdbus_arg - information and state of a single ioctl command item ++ * @type: item type ++ * @item: set by the parser to the first found item of this type ++ * @multiple: whether multiple items of this type are allowed ++ * @mandatory: whether at least one item of this type is required ++ * ++ * This structure describes a single item in an ioctl command payload. The ++ * caller has to pre-fill the type and flags, the parser will then use this ++ * information to verify the ioctl payload. @item is set by the parser to point ++ * to the first occurrence of the item. ++ */ ++struct kdbus_arg { ++ u64 type; ++ struct kdbus_item *item; ++ bool multiple : 1; ++ bool mandatory : 1; ++}; ++ ++/** ++ * kdbus_args - information and state of ioctl command parser ++ * @allowed_flags: set of flags this command supports ++ * @argc: number of items in @argv ++ * @argv: array of items this command supports ++ * @user: set by parser to user-space location of current command ++ * @cmd: set by parser to kernel copy of command payload ++ * @items: points to item array in @cmd ++ * @items_size: size of @items in bytes ++ * ++ * This structure is used to parse ioctl command payloads on each invocation. ++ * The ioctl handler has to pre-fill the flags and allowed items before passing ++ * the object to kdbus_args_parse(). The parser will copy the command payload ++ * into kernel-space and verify the correctness of the data. ++ */ ++struct kdbus_args { ++ u64 allowed_flags; ++ size_t argc; ++ struct kdbus_arg *argv; ++ ++ struct kdbus_cmd __user *user; ++ struct kdbus_cmd *cmd; ++ ++ struct kdbus_item *items; ++ size_t items_size; ++}; ++ ++int __kdbus_args_parse(struct kdbus_args *args, void __user *argp, ++ size_t type_size, size_t items_offset, void **out); ++int kdbus_args_clear(struct kdbus_args *args, int ret); ++ ++#define kdbus_args_parse(_args, _argp, _v) \ ++ ({ \ ++ BUILD_BUG_ON(offsetof(typeof(**(_v)), size) != \ ++ offsetof(struct kdbus_cmd, size)); \ ++ BUILD_BUG_ON(offsetof(typeof(**(_v)), flags) != \ ++ offsetof(struct kdbus_cmd, flags)); \ ++ BUILD_BUG_ON(offsetof(typeof(**(_v)), return_flags) != \ ++ offsetof(struct kdbus_cmd, return_flags)); \ ++ __kdbus_args_parse((_args), (_argp), sizeof(**(_v)), \ ++ offsetof(typeof(**(_v)), items), \ ++ (void **)(_v)); \ ++ }) ++ ++#endif +diff --git a/ipc/kdbus/limits.h b/ipc/kdbus/limits.h +new file mode 100644 +index 000000000000..6450f58cffcf +--- /dev/null ++++ b/ipc/kdbus/limits.h +@@ -0,0 +1,64 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#ifndef __KDBUS_DEFAULTS_H ++#define __KDBUS_DEFAULTS_H ++ ++#include <linux/kernel.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 memfd items per message */ ++#define KDBUS_MSG_MAX_MEMFD_ITEMS 16 ++ ++/* max size of ioctl command data */ ++#define KDBUS_CMD_MAX_SIZE SZ_32K ++ ++/* maximum number of inflight fds in a target queue per user */ ++#define KDBUS_CONN_MAX_FDS_PER_USER 16 ++ ++/* 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 number of matches per connection */ ++#define KDBUS_MATCH_MAX 256 ++ ++/* maximum number of queued messages from the same individual user */ ++#define KDBUS_CONN_MAX_MSGS 256 ++ ++/* maximum number of well-known names per connection */ ++#define KDBUS_CONN_MAX_NAMES 256 ++ ++/* 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 1024 ++ ++/* maximum number of buses per user in one domain */ ++#define KDBUS_USER_MAX_BUSES 16 ++ ++#endif +diff --git a/ipc/kdbus/main.c b/ipc/kdbus/main.c +new file mode 100644 +index 000000000000..785f529d98b7 +--- /dev/null ++++ b/ipc/kdbus/main.c +@@ -0,0 +1,125 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++#include <linux/fs.h> ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++ ++#include "util.h" ++#include "fs.h" ++#include "handle.h" ++#include "metadata.h" ++#include "node.h" ++ ++/* ++ * This is a simplified outline of the internal kdbus object relations, for ++ * those interested in the inner life of the driver implementation. ++ * ++ * From a mount point's (domain's) perspective: ++ * ++ * struct kdbus_domain ++ * |» struct kdbus_user *user (many, owned) ++ * '» struct kdbus_node node (embedded) ++ * |» struct kdbus_node children (many, referenced) ++ * |» struct kdbus_node *parent (pinned) ++ * '» struct kdbus_bus (many, pinned) ++ * |» struct kdbus_node node (embedded) ++ * '» struct kdbus_ep (many, pinned) ++ * |» struct kdbus_node node (embedded) ++ * |» struct kdbus_bus *bus (pinned) ++ * |» struct kdbus_conn conn_list (many, pinned) ++ * | |» struct kdbus_ep *ep (pinned) ++ * | |» struct kdbus_name_entry *activator_of (owned) ++ * | |» struct kdbus_match_db *match_db (owned) ++ * | |» struct kdbus_meta *meta (owned) ++ * | |» struct kdbus_match_db *match_db (owned) ++ * | | '» struct kdbus_match_entry (many, owned) ++ * | | ++ * | |» struct kdbus_pool *pool (owned) ++ * | | '» struct kdbus_pool_slice *slices (many, owned) ++ * | | '» struct kdbus_pool *pool (pinned) ++ * | | ++ * | |» struct kdbus_user *user (pinned) ++ * | `» struct kdbus_queue_entry entries (many, embedded) ++ * | |» struct kdbus_pool_slice *slice (pinned) ++ * | |» struct kdbus_conn_reply *reply (owned) ++ * | '» struct kdbus_user *user (pinned) ++ * | ++ * '» struct kdbus_user *user (pinned) ++ * '» struct kdbus_policy_db policy_db (embedded) ++ * |» struct kdbus_policy_db_entry (many, owned) ++ * | |» struct kdbus_conn (pinned) ++ * | '» struct kdbus_ep (pinned) ++ * | ++ * '» struct kdbus_policy_db_cache_entry (many, owned) ++ * '» struct kdbus_conn (pinned) ++ * ++ * For the life-time of a file descriptor derived from calling open() on a file ++ * inside the mount point: ++ * ++ * struct kdbus_handle ++ * |» struct kdbus_meta *meta (owned) ++ * |» struct kdbus_ep *ep (pinned) ++ * |» struct kdbus_conn *conn (owned) ++ * '» struct kdbus_ep *ep (owned) ++ */ ++ ++/* kdbus mount-point /sys/fs/kdbus */ ++static struct kobject *kdbus_dir; ++ ++/* global module option to apply a mask to exported metadata */ ++unsigned long long kdbus_meta_attach_mask = KDBUS_ATTACH_TIMESTAMP | ++ KDBUS_ATTACH_CREDS | ++ KDBUS_ATTACH_PIDS | ++ KDBUS_ATTACH_AUXGROUPS | ++ KDBUS_ATTACH_NAMES | ++ KDBUS_ATTACH_SECLABEL | ++ KDBUS_ATTACH_CONN_DESCRIPTION; ++MODULE_PARM_DESC(attach_flags_mask, "Attach-flags mask for exported metadata"); ++module_param_named(attach_flags_mask, kdbus_meta_attach_mask, ullong, 0644); ++ ++static int __init kdbus_init(void) ++{ ++ int ret; ++ ++ kdbus_dir = kobject_create_and_add(KBUILD_MODNAME, fs_kobj); ++ if (!kdbus_dir) ++ return -ENOMEM; ++ ++ ret = kdbus_fs_init(); ++ if (ret < 0) { ++ pr_err("cannot register filesystem: %d\n", ret); ++ goto exit_dir; ++ } ++ ++ pr_info("initialized\n"); ++ return 0; ++ ++exit_dir: ++ kobject_put(kdbus_dir); ++ return ret; ++} ++ ++static void __exit kdbus_exit(void) ++{ ++ kdbus_fs_exit(); ++ kobject_put(kdbus_dir); ++} ++ ++module_init(kdbus_init); ++module_exit(kdbus_exit); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("D-Bus, powerful, easy to use interprocess communication"); ++MODULE_ALIAS_FS(KBUILD_MODNAME "fs"); +diff --git a/ipc/kdbus/util.c b/ipc/kdbus/util.c +new file mode 100644 +index 000000000000..eaa806a27997 +--- /dev/null ++++ b/ipc/kdbus/util.c +@@ -0,0 +1,201 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> ++ * ++ * 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/cred.h> ++#include <linux/ctype.h> ++#include <linux/err.h> ++#include <linux/file.h> ++#include <linux/slab.h> ++#include <linux/string.h> ++#include <linux/uaccess.h> ++#include <linux/uio.h> ++#include <linux/user_namespace.h> ++ ++#include "limits.h" ++#include "util.h" ++ ++/** ++ * kdbus_copy_from_user() - copy aligned data from user-space ++ * @dest: target buffer in kernel memory ++ * @user_ptr: user-provided source buffer ++ * @size: memory size to copy from user ++ * ++ * This copies @size bytes from @user_ptr into the kernel, just like ++ * copy_from_user() does. But we enforce an 8-byte alignment and reject any ++ * unaligned user-space pointers. ++ * ++ * Return: 0 on success, negative error code on failure. ++ */ ++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; ++} ++ ++/** ++ * kdbus_memdup_user() - copy dynamically sized object from user-space ++ * @user_ptr: user-provided source buffer ++ * @sz_min: minimum object size ++ * @sz_max: maximum object size ++ * ++ * This copies a dynamically sized object from user-space into kernel-space. We ++ * require the object to have a 64bit size field at offset 0. We read it out ++ * first, allocate a suitably sized buffer and then copy all data. ++ * ++ * The @sz_min and @sz_max parameters define possible min and max object sizes ++ * so user-space cannot trigger un-bound kernel-space allocations. ++ * ++ * The same alignment-restrictions as described in kdbus_copy_from_user() apply. ++ * ++ * Return: pointer to dynamically allocated copy, or ERR_PTR() on failure. ++ */ ++void *kdbus_memdup_user(void __user *user_ptr, size_t sz_min, size_t sz_max) ++{ ++ void *ptr; ++ u64 size; ++ int ret; ++ ++ ret = kdbus_copy_from_user(&size, user_ptr, sizeof(size)); ++ if (ret < 0) ++ return ERR_PTR(ret); ++ ++ if (size < sz_min) ++ return ERR_PTR(-EINVAL); ++ ++ if (size > sz_max) ++ return ERR_PTR(-EMSGSIZE); ++ ++ ptr = memdup_user(user_ptr, size); ++ if (IS_ERR(ptr)) ++ return ptr; ++ ++ if (*(u64 *)ptr != size) { ++ kfree(ptr); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ return ptr; ++} ++ ++/** ++ * kdbus_verify_uid_prefix() - verify UID prefix of a user-supplied name ++ * @name: user-supplied name to verify ++ * @user_ns: user-namespace to act in ++ * @kuid: Kernel internal uid of user ++ * ++ * This verifies that the user-supplied name @name has their UID as prefix. This ++ * is the default name-spacing policy we enforce on user-supplied names for ++ * public kdbus entities like buses and endpoints. ++ * ++ * The user must supply names prefixed with "<UID>-", whereas the UID is ++ * interpreted in the user-namespace of the domain. If the user fails to supply ++ * such a prefixed name, we reject it. ++ * ++ * Return: 0 on success, negative error code on failure ++ */ ++int kdbus_verify_uid_prefix(const char *name, struct user_namespace *user_ns, ++ kuid_t kuid) ++{ ++ uid_t uid; ++ char prefix[16]; ++ ++ /* ++ * The kuid must have a mapping into the userns of the domain ++ * otherwise do not allow creation of buses nor endpoints. ++ */ ++ uid = from_kuid(user_ns, kuid); ++ if (uid == (uid_t) -1) ++ return -EINVAL; ++ ++ snprintf(prefix, sizeof(prefix), "%u-", uid); ++ if (strncmp(name, prefix, strlen(prefix)) != 0) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++/** ++ * kdbus_sanitize_attach_flags() - Sanitize attach flags from user-space ++ * @flags: Attach flags provided by userspace ++ * @attach_flags: A pointer where to store the valid attach flags ++ * ++ * Convert attach-flags provided by user-space into a valid mask. If the mask ++ * is invalid, an error is returned. The sanitized attach flags are stored in ++ * the output parameter. ++ * ++ * Return: 0 on success, negative error on failure. ++ */ ++int kdbus_sanitize_attach_flags(u64 flags, u64 *attach_flags) ++{ ++ /* 'any' degrades to 'all' for compatibility */ ++ if (flags == _KDBUS_ATTACH_ANY) ++ flags = _KDBUS_ATTACH_ALL; ++ ++ /* reject unknown attach flags */ ++ if (flags & ~_KDBUS_ATTACH_ALL) ++ return -EINVAL; ++ ++ *attach_flags = flags; ++ return 0; ++} ++ ++/** ++ * kdbus_kvec_set - helper utility to assemble kvec arrays ++ * @kvec: kvec entry to use ++ * @src: Source address to set in @kvec ++ * @len: Number of bytes in @src ++ * @total_len: Pointer to total length variable ++ * ++ * Set @src and @len in @kvec, and increase @total_len by @len. ++ */ ++void kdbus_kvec_set(struct kvec *kvec, void *src, size_t len, u64 *total_len) ++{ ++ kvec->iov_base = src; ++ kvec->iov_len = len; ++ *total_len += len; ++} ++ ++static const char * const zeros = "\0\0\0\0\0\0\0"; ++ ++/** ++ * kdbus_kvec_pad - conditionally write a padding kvec ++ * @kvec: kvec entry to use ++ * @len: Total length used for kvec array ++ * ++ * Check if the current total byte length of the array in @len is aligned to ++ * 8 bytes. If it isn't, fill @kvec with padding information and increase @len ++ * by the number of bytes stored in @kvec. ++ * ++ * Return: the number of added padding bytes. ++ */ ++size_t kdbus_kvec_pad(struct kvec *kvec, u64 *len) ++{ ++ size_t pad = KDBUS_ALIGN8(*len) - *len; ++ ++ if (!pad) ++ return 0; ++ ++ kvec->iov_base = (void *)zeros; ++ kvec->iov_len = pad; ++ ++ *len += pad; ++ ++ return pad; ++} +diff --git a/ipc/kdbus/util.h b/ipc/kdbus/util.h +new file mode 100644 +index 000000000000..9caadb337912 +--- /dev/null ++++ b/ipc/kdbus/util.h +@@ -0,0 +1,74 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> ++ * ++ * 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_member_set_user - write a structure member to user memory ++ * @_s: Variable to copy from ++ * @_b: Buffer to write to ++ * @_t: Structure type ++ * @_m: Member name in the passed structure ++ * ++ * Return: the result of copy_to_user() ++ */ ++#define kdbus_member_set_user(_s, _b, _t, _m) \ ++({ \ ++ u64 __user *_sz = \ ++ (void __user *)((u8 __user *)(_b) + offsetof(_t, _m)); \ ++ copy_to_user(_sz, _s, sizeof(((_t *)0)->_m)); \ ++}) ++ ++/** ++ * kdbus_strhash - calculate a hash ++ * @str: String ++ * ++ * Return: hash value ++ */ ++static inline unsigned int kdbus_strhash(const char *str) ++{ ++ unsigned long hash = init_name_hash(); ++ ++ while (*str) ++ hash = partial_name_hash(*str++, hash); ++ ++ return end_name_hash(hash); ++} ++ ++int kdbus_verify_uid_prefix(const char *name, struct user_namespace *user_ns, ++ kuid_t kuid); ++int kdbus_sanitize_attach_flags(u64 flags, u64 *attach_flags); ++ ++int kdbus_copy_from_user(void *dest, void __user *user_ptr, size_t size); ++void *kdbus_memdup_user(void __user *user_ptr, size_t sz_min, size_t sz_max); ++ ++struct kvec; ++ ++void kdbus_kvec_set(struct kvec *kvec, void *src, size_t len, u64 *total_len); ++size_t kdbus_kvec_pad(struct kvec *kvec, u64 *len); ++ ++#endif diff --git a/kdbus-add-name-registry-implementation.patch b/kdbus-add-name-registry-implementation.patch new file mode 100644 index 000000000..b45e1ad9e --- /dev/null +++ b/kdbus-add-name-registry-implementation.patch @@ -0,0 +1,883 @@ +From: Daniel Mack <daniel@zonque.org> +Date: Thu, 11 Sep 2014 19:00:00 +0200 +Subject: [PATCH] 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: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Djalal Harouni <tixxdz@opendz.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + ipc/kdbus/names.c | 772 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ + ipc/kdbus/names.h | 74 ++++++ + 2 files changed, 846 insertions(+) + create mode 100644 ipc/kdbus/names.c + create mode 100644 ipc/kdbus/names.h + +diff --git a/ipc/kdbus/names.c b/ipc/kdbus/names.c +new file mode 100644 +index 000000000000..657008e1bb37 +--- /dev/null ++++ b/ipc/kdbus/names.c +@@ -0,0 +1,772 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#include <linux/ctype.h> ++#include <linux/fs.h> ++#include <linux/hash.h> ++#include <linux/idr.h> ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/mutex.h> ++#include <linux/rwsem.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/uaccess.h> ++#include <linux/uio.h> ++ ++#include "bus.h" ++#include "connection.h" ++#include "endpoint.h" ++#include "handle.h" ++#include "item.h" ++#include "names.h" ++#include "notify.h" ++#include "policy.h" ++ ++struct kdbus_name_pending { ++ u64 flags; ++ struct kdbus_conn *conn; ++ struct kdbus_name_entry *name; ++ struct list_head conn_entry; ++ struct list_head name_entry; ++}; ++ ++static int kdbus_name_pending_new(struct kdbus_name_entry *e, ++ struct kdbus_conn *conn, u64 flags) ++{ ++ struct kdbus_name_pending *p; ++ ++ kdbus_conn_assert_active(conn); ++ ++ p = kmalloc(sizeof(*p), GFP_KERNEL); ++ if (!p) ++ return -ENOMEM; ++ ++ p->flags = flags; ++ p->conn = conn; ++ p->name = e; ++ list_add_tail(&p->conn_entry, &conn->names_queue_list); ++ list_add_tail(&p->name_entry, &e->queue); ++ ++ return 0; ++} ++ ++static void kdbus_name_pending_free(struct kdbus_name_pending *p) ++{ ++ if (!p) ++ return; ++ ++ list_del(&p->name_entry); ++ list_del(&p->conn_entry); ++ kfree(p); ++} ++ ++static struct kdbus_name_entry * ++kdbus_name_entry_new(struct kdbus_name_registry *r, u32 hash, const char *name) ++{ ++ struct kdbus_name_entry *e; ++ size_t namelen; ++ ++ namelen = strlen(name); ++ ++ e = kmalloc(sizeof(*e) + namelen + 1, GFP_KERNEL); ++ if (!e) ++ return ERR_PTR(-ENOMEM); ++ ++ e->name_id = ++r->name_seq_last; ++ e->flags = 0; ++ e->conn = NULL; ++ e->activator = NULL; ++ INIT_LIST_HEAD(&e->queue); ++ INIT_LIST_HEAD(&e->conn_entry); ++ hash_add(r->entries_hash, &e->hentry, hash); ++ memcpy(e->name, name, namelen + 1); ++ ++ return e; ++} ++ ++static void kdbus_name_entry_free(struct kdbus_name_entry *e) ++{ ++ if (!e) ++ return; ++ ++ WARN_ON(!list_empty(&e->conn_entry)); ++ WARN_ON(!list_empty(&e->queue)); ++ WARN_ON(e->activator); ++ WARN_ON(e->conn); ++ ++ hash_del(&e->hentry); ++ kfree(e); ++} ++ ++static void kdbus_name_entry_set_owner(struct kdbus_name_entry *e, ++ struct kdbus_conn *conn, u64 flags) ++{ ++ WARN_ON(e->conn); ++ ++ e->conn = kdbus_conn_ref(conn); ++ e->flags = flags; ++ atomic_inc(&conn->name_count); ++ list_add_tail(&e->conn_entry, &e->conn->names_list); ++} ++ ++static void kdbus_name_entry_remove_owner(struct kdbus_name_entry *e) ++{ ++ WARN_ON(!e->conn); ++ ++ list_del_init(&e->conn_entry); ++ atomic_dec(&e->conn->name_count); ++ e->flags = 0; ++ e->conn = kdbus_conn_unref(e->conn); ++} ++ ++static void kdbus_name_entry_replace_owner(struct kdbus_name_entry *e, ++ struct kdbus_conn *conn, u64 flags) ++{ ++ if (WARN_ON(!e->conn) || WARN_ON(conn == e->conn)) ++ return; ++ ++ kdbus_notify_name_change(conn->ep->bus, KDBUS_ITEM_NAME_CHANGE, ++ e->conn->id, conn->id, ++ e->flags, flags, e->name); ++ kdbus_name_entry_remove_owner(e); ++ kdbus_name_entry_set_owner(e, conn, flags); ++} ++ ++/** ++ * kdbus_name_is_valid() - check if a name is valid ++ * @p: The name to check ++ * @allow_wildcard: Whether or not to allow a wildcard name ++ * ++ * A name is valid if all of the following criterias are met: ++ * ++ * - The name has two or more elements separated by a period ('.') character. ++ * - All elements must contain at least one character. ++ * - Each element must only contain the ASCII characters "[A-Z][a-z][0-9]_-" ++ * and must not begin with a digit. ++ * - The name must not exceed KDBUS_NAME_MAX_LEN. ++ * - If @allow_wildcard is true, the name may end on '.*' ++ */ ++bool kdbus_name_is_valid(const char *p, bool allow_wildcard) ++{ ++ bool dot, found_dot = false; ++ const char *q; ++ ++ for (dot = true, q = p; *q; q++) { ++ if (*q == '.') { ++ if (dot) ++ return false; ++ ++ found_dot = true; ++ dot = true; ++ } else { ++ bool good; ++ ++ good = isalpha(*q) || (!dot && isdigit(*q)) || ++ *q == '_' || *q == '-' || ++ (allow_wildcard && dot && ++ *q == '*' && *(q + 1) == '\0'); ++ ++ if (!good) ++ return false; ++ ++ dot = false; ++ } ++ } ++ ++ if (q - p > KDBUS_NAME_MAX_LEN) ++ return false; ++ ++ if (dot) ++ return false; ++ ++ if (!found_dot) ++ return false; ++ ++ return true; ++} ++ ++/** ++ * kdbus_name_registry_new() - create a new name registry ++ * ++ * Return: a new kdbus_name_registry on success, ERR_PTR on failure. ++ */ ++struct kdbus_name_registry *kdbus_name_registry_new(void) ++{ ++ struct kdbus_name_registry *r; ++ ++ r = kmalloc(sizeof(*r), GFP_KERNEL); ++ if (!r) ++ return ERR_PTR(-ENOMEM); ++ ++ hash_init(r->entries_hash); ++ init_rwsem(&r->rwlock); ++ r->name_seq_last = 0; ++ ++ return r; ++} ++ ++/** ++ * kdbus_name_registry_free() - drop a name reg's reference ++ * @reg: The name registry, may be %NULL ++ * ++ * Cleanup the name registry's internal structures. ++ */ ++void kdbus_name_registry_free(struct kdbus_name_registry *reg) ++{ ++ if (!reg) ++ return; ++ ++ WARN_ON(!hash_empty(reg->entries_hash)); ++ kfree(reg); ++} ++ ++static struct kdbus_name_entry * ++kdbus_name_find(struct kdbus_name_registry *reg, u32 hash, const char *name) ++{ ++ struct kdbus_name_entry *e; ++ ++ lockdep_assert_held(®->rwlock); ++ ++ hash_for_each_possible(reg->entries_hash, e, hentry, hash) ++ if (strcmp(e->name, name) == 0) ++ return e; ++ ++ return NULL; ++} ++ ++/** ++ * kdbus_name_lookup_unlocked() - lookup name in registry ++ * @reg: name registry ++ * @name: name to lookup ++ * ++ * This looks up @name in the given name-registry and returns the ++ * kdbus_name_entry object. The caller must hold the registry-lock and must not ++ * access the returned object after releasing the lock. ++ * ++ * Return: Pointer to name-entry, or NULL if not found. ++ */ ++struct kdbus_name_entry * ++kdbus_name_lookup_unlocked(struct kdbus_name_registry *reg, const char *name) ++{ ++ return kdbus_name_find(reg, kdbus_strhash(name), name); ++} ++ ++/** ++ * kdbus_name_acquire() - acquire a name ++ * @reg: The name registry ++ * @conn: The connection to pin this entry to ++ * @name: The name to acquire ++ * @flags: Acquisition flags (KDBUS_NAME_*) ++ * @return_flags: Pointer to return flags for the acquired name ++ * (KDBUS_NAME_*), may be %NULL ++ * ++ * Callers must ensure that @conn is either a privileged bus user or has ++ * sufficient privileges in the policy-db to own the well-known name @name. ++ * ++ * Return: 0 success, negative error number on failure. ++ */ ++int kdbus_name_acquire(struct kdbus_name_registry *reg, ++ struct kdbus_conn *conn, const char *name, ++ u64 flags, u64 *return_flags) ++{ ++ struct kdbus_name_entry *e; ++ u64 rflags = 0; ++ int ret = 0; ++ u32 hash; ++ ++ kdbus_conn_assert_active(conn); ++ ++ down_write(®->rwlock); ++ ++ if (!kdbus_conn_policy_own_name(conn, current_cred(), name)) { ++ ret = -EPERM; ++ goto exit_unlock; ++ } ++ ++ hash = kdbus_strhash(name); ++ e = kdbus_name_find(reg, hash, name); ++ if (!e) { ++ /* claim new name */ ++ ++ if (conn->activator_of) { ++ ret = -EINVAL; ++ goto exit_unlock; ++ } ++ ++ e = kdbus_name_entry_new(reg, hash, name); ++ if (IS_ERR(e)) { ++ ret = PTR_ERR(e); ++ goto exit_unlock; ++ } ++ ++ if (kdbus_conn_is_activator(conn)) { ++ e->activator = kdbus_conn_ref(conn); ++ conn->activator_of = e; ++ } ++ ++ kdbus_name_entry_set_owner(e, conn, flags); ++ kdbus_notify_name_change(e->conn->ep->bus, KDBUS_ITEM_NAME_ADD, ++ 0, e->conn->id, 0, e->flags, e->name); ++ } else if (e->conn == conn || e == conn->activator_of) { ++ /* connection already owns that name */ ++ ret = -EALREADY; ++ } else if (kdbus_conn_is_activator(conn)) { ++ /* activator claims existing name */ ++ ++ if (conn->activator_of) { ++ ret = -EINVAL; /* multiple names not allowed */ ++ } else if (e->activator) { ++ ret = -EEXIST; /* only one activator per name */ ++ } else { ++ e->activator = kdbus_conn_ref(conn); ++ conn->activator_of = e; ++ } ++ } else if (e->flags & KDBUS_NAME_ACTIVATOR) { ++ /* claim name of an activator */ ++ ++ kdbus_conn_move_messages(conn, e->activator, 0); ++ kdbus_name_entry_replace_owner(e, conn, flags); ++ } else if ((flags & KDBUS_NAME_REPLACE_EXISTING) && ++ (e->flags & KDBUS_NAME_ALLOW_REPLACEMENT)) { ++ /* claim name of a previous owner */ ++ ++ if (e->flags & KDBUS_NAME_QUEUE) { ++ /* move owner back to queue if they asked for it */ ++ ret = kdbus_name_pending_new(e, e->conn, e->flags); ++ if (ret < 0) ++ goto exit_unlock; ++ } ++ ++ kdbus_name_entry_replace_owner(e, conn, flags); ++ } else if (flags & KDBUS_NAME_QUEUE) { ++ /* add to waiting-queue of the name */ ++ ++ ret = kdbus_name_pending_new(e, conn, flags); ++ if (ret >= 0) ++ /* tell the caller that we queued it */ ++ rflags |= KDBUS_NAME_IN_QUEUE; ++ } else { ++ /* the name is busy, return a failure */ ++ ret = -EEXIST; ++ } ++ ++ if (ret == 0 && return_flags) ++ *return_flags = rflags; ++ ++exit_unlock: ++ up_write(®->rwlock); ++ kdbus_notify_flush(conn->ep->bus); ++ return ret; ++} ++ ++static void kdbus_name_release_unlocked(struct kdbus_name_registry *reg, ++ struct kdbus_name_entry *e) ++{ ++ struct kdbus_name_pending *p; ++ ++ lockdep_assert_held(®->rwlock); ++ ++ p = list_first_entry_or_null(&e->queue, struct kdbus_name_pending, ++ name_entry); ++ ++ if (p) { ++ /* give it to first active waiter in the queue */ ++ kdbus_name_entry_replace_owner(e, p->conn, p->flags); ++ kdbus_name_pending_free(p); ++ } else if (e->activator && e->activator != e->conn) { ++ /* hand it back to an active activator connection */ ++ kdbus_conn_move_messages(e->activator, e->conn, e->name_id); ++ kdbus_name_entry_replace_owner(e, e->activator, ++ KDBUS_NAME_ACTIVATOR); ++ } else { ++ /* release the name */ ++ kdbus_notify_name_change(e->conn->ep->bus, ++ KDBUS_ITEM_NAME_REMOVE, ++ e->conn->id, 0, e->flags, 0, e->name); ++ kdbus_name_entry_remove_owner(e); ++ kdbus_name_entry_free(e); ++ } ++} ++ ++static int kdbus_name_release(struct kdbus_name_registry *reg, ++ struct kdbus_conn *conn, ++ const char *name) ++{ ++ struct kdbus_name_pending *p; ++ struct kdbus_name_entry *e; ++ int ret = 0; ++ ++ down_write(®->rwlock); ++ e = kdbus_name_find(reg, kdbus_strhash(name), name); ++ if (!e) { ++ ret = -ESRCH; ++ } else if (e->conn == conn) { ++ kdbus_name_release_unlocked(reg, e); ++ } else { ++ ret = -EADDRINUSE; ++ list_for_each_entry(p, &e->queue, name_entry) { ++ if (p->conn == conn) { ++ kdbus_name_pending_free(p); ++ ret = 0; ++ break; ++ } ++ } ++ } ++ up_write(®->rwlock); ++ ++ kdbus_notify_flush(conn->ep->bus); ++ return ret; ++} ++ ++/** ++ * kdbus_name_release_all() - remove all name entries of a given connection ++ * @reg: name registry ++ * @conn: connection ++ */ ++void kdbus_name_release_all(struct kdbus_name_registry *reg, ++ struct kdbus_conn *conn) ++{ ++ struct kdbus_name_pending *p; ++ struct kdbus_conn *activator = NULL; ++ struct kdbus_name_entry *e; ++ ++ down_write(®->rwlock); ++ ++ if (kdbus_conn_is_activator(conn)) { ++ activator = conn->activator_of->activator; ++ conn->activator_of->activator = NULL; ++ } ++ ++ while ((p = list_first_entry_or_null(&conn->names_queue_list, ++ struct kdbus_name_pending, ++ conn_entry))) ++ kdbus_name_pending_free(p); ++ while ((e = list_first_entry_or_null(&conn->names_list, ++ struct kdbus_name_entry, ++ conn_entry))) ++ kdbus_name_release_unlocked(reg, e); ++ ++ up_write(®->rwlock); ++ ++ kdbus_conn_unref(activator); ++ kdbus_notify_flush(conn->ep->bus); ++} ++ ++/** ++ * kdbus_cmd_name_acquire() - handle KDBUS_CMD_NAME_ACQUIRE ++ * @conn: connection to operate on ++ * @argp: command payload ++ * ++ * Return: 0 on success, negative error code on failure. ++ */ ++int kdbus_cmd_name_acquire(struct kdbus_conn *conn, void __user *argp) ++{ ++ const char *item_name; ++ struct kdbus_cmd *cmd; ++ int ret; ++ ++ struct kdbus_arg argv[] = { ++ { .type = KDBUS_ITEM_NEGOTIATE }, ++ { .type = KDBUS_ITEM_NAME, .mandatory = true }, ++ }; ++ struct kdbus_args args = { ++ .allowed_flags = KDBUS_FLAG_NEGOTIATE | ++ KDBUS_NAME_REPLACE_EXISTING | ++ KDBUS_NAME_ALLOW_REPLACEMENT | ++ KDBUS_NAME_QUEUE, ++ .argv = argv, ++ .argc = ARRAY_SIZE(argv), ++ }; ++ ++ if (!kdbus_conn_is_ordinary(conn)) ++ return -EOPNOTSUPP; ++ ++ ret = kdbus_args_parse(&args, argp, &cmd); ++ if (ret != 0) ++ return ret; ++ ++ item_name = argv[1].item->str; ++ if (!kdbus_name_is_valid(item_name, false)) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ /* ++ * Do atomic_inc_return here to reserve our slot, then decrement ++ * it before returning. ++ */ ++ if (atomic_inc_return(&conn->name_count) > KDBUS_CONN_MAX_NAMES) { ++ ret = -E2BIG; ++ goto exit_dec; ++ } ++ ++ ret = kdbus_name_acquire(conn->ep->bus->name_registry, conn, item_name, ++ cmd->flags, &cmd->return_flags); ++ if (ret < 0) ++ goto exit_dec; ++ ++exit_dec: ++ atomic_dec(&conn->name_count); ++exit: ++ return kdbus_args_clear(&args, ret); ++} ++ ++/** ++ * kdbus_cmd_name_release() - handle KDBUS_CMD_NAME_RELEASE ++ * @conn: connection to operate on ++ * @argp: command payload ++ * ++ * Return: 0 on success, negative error code on failure. ++ */ ++int kdbus_cmd_name_release(struct kdbus_conn *conn, void __user *argp) ++{ ++ struct kdbus_cmd *cmd; ++ int ret; ++ ++ struct kdbus_arg argv[] = { ++ { .type = KDBUS_ITEM_NEGOTIATE }, ++ { .type = KDBUS_ITEM_NAME, .mandatory = true }, ++ }; ++ struct kdbus_args args = { ++ .allowed_flags = KDBUS_FLAG_NEGOTIATE, ++ .argv = argv, ++ .argc = ARRAY_SIZE(argv), ++ }; ++ ++ if (!kdbus_conn_is_ordinary(conn)) ++ return -EOPNOTSUPP; ++ ++ ret = kdbus_args_parse(&args, argp, &cmd); ++ if (ret != 0) ++ return ret; ++ ++ ret = kdbus_name_release(conn->ep->bus->name_registry, conn, ++ argv[1].item->str); ++ return kdbus_args_clear(&args, ret); ++} ++ ++static int kdbus_list_write(struct kdbus_conn *conn, ++ struct kdbus_conn *c, ++ struct kdbus_pool_slice *slice, ++ size_t *pos, ++ struct kdbus_name_entry *e, ++ bool write) ++{ ++ struct kvec kvec[4]; ++ size_t cnt = 0; ++ int ret; ++ ++ /* info header */ ++ struct kdbus_info info = { ++ .size = 0, ++ .id = c->id, ++ .flags = c->flags, ++ }; ++ ++ /* fake the header of a kdbus_name item */ ++ struct { ++ u64 size; ++ u64 type; ++ u64 flags; ++ } h = {}; ++ ++ if (e && !kdbus_conn_policy_see_name_unlocked(conn, current_cred(), ++ e->name)) ++ return 0; ++ ++ kdbus_kvec_set(&kvec[cnt++], &info, sizeof(info), &info.size); ++ ++ /* append name */ ++ if (e) { ++ size_t slen = strlen(e->name) + 1; ++ ++ h.size = offsetof(struct kdbus_item, name.name) + slen; ++ h.type = KDBUS_ITEM_OWNED_NAME; ++ h.flags = e->flags; ++ ++ kdbus_kvec_set(&kvec[cnt++], &h, sizeof(h), &info.size); ++ kdbus_kvec_set(&kvec[cnt++], e->name, slen, &info.size); ++ cnt += !!kdbus_kvec_pad(&kvec[cnt], &info.size); ++ } ++ ++ if (write) { ++ ret = kdbus_pool_slice_copy_kvec(slice, *pos, kvec, ++ cnt, info.size); ++ if (ret < 0) ++ return ret; ++ } ++ ++ *pos += info.size; ++ return 0; ++} ++ ++static int kdbus_list_all(struct kdbus_conn *conn, u64 flags, ++ struct kdbus_pool_slice *slice, ++ size_t *pos, bool write) ++{ ++ struct kdbus_conn *c; ++ size_t p = *pos; ++ int ret, i; ++ ++ hash_for_each(conn->ep->bus->conn_hash, i, c, hentry) { ++ bool added = false; ++ ++ /* skip monitors */ ++ if (kdbus_conn_is_monitor(c)) ++ continue; ++ ++ /* skip activators */ ++ if (!(flags & KDBUS_LIST_ACTIVATORS) && ++ kdbus_conn_is_activator(c)) ++ continue; ++ ++ /* all names the connection owns */ ++ if (flags & (KDBUS_LIST_NAMES | KDBUS_LIST_ACTIVATORS)) { ++ struct kdbus_name_entry *e; ++ ++ list_for_each_entry(e, &c->names_list, conn_entry) { ++ struct kdbus_conn *a = e->activator; ++ ++ if ((flags & KDBUS_LIST_ACTIVATORS) && ++ a && a != c) { ++ ret = kdbus_list_write(conn, a, slice, ++ &p, e, write); ++ if (ret < 0) { ++ mutex_unlock(&c->lock); ++ return ret; ++ } ++ ++ added = true; ++ } ++ ++ if (flags & KDBUS_LIST_NAMES || ++ kdbus_conn_is_activator(c)) { ++ ret = kdbus_list_write(conn, c, slice, ++ &p, e, write); ++ if (ret < 0) { ++ mutex_unlock(&c->lock); ++ return ret; ++ } ++ ++ added = true; ++ } ++ } ++ } ++ ++ /* queue of names the connection is currently waiting for */ ++ if (flags & KDBUS_LIST_QUEUED) { ++ struct kdbus_name_pending *q; ++ ++ list_for_each_entry(q, &c->names_queue_list, ++ conn_entry) { ++ ret = kdbus_list_write(conn, c, slice, &p, ++ q->name, write); ++ if (ret < 0) { ++ mutex_unlock(&c->lock); ++ return ret; ++ } ++ ++ added = true; ++ } ++ } ++ ++ /* nothing added so far, just add the unique ID */ ++ if (!added && flags & KDBUS_LIST_UNIQUE) { ++ ret = kdbus_list_write(conn, c, slice, &p, NULL, write); ++ if (ret < 0) ++ return ret; ++ } ++ } ++ ++ *pos = p; ++ return 0; ++} ++ ++/** ++ * kdbus_cmd_list() - handle KDBUS_CMD_LIST ++ * @conn: connection to operate on ++ * @argp: command payload ++ * ++ * Return: 0 on success, negative error code on failure. ++ */ ++int kdbus_cmd_list(struct kdbus_conn *conn, void __user *argp) ++{ ++ struct kdbus_name_registry *reg = conn->ep->bus->name_registry; ++ struct kdbus_pool_slice *slice = NULL; ++ struct kdbus_cmd_list *cmd; ++ size_t pos, size; ++ int ret; ++ ++ struct kdbus_arg argv[] = { ++ { .type = KDBUS_ITEM_NEGOTIATE }, ++ }; ++ struct kdbus_args args = { ++ .allowed_flags = KDBUS_FLAG_NEGOTIATE | ++ KDBUS_LIST_UNIQUE | ++ KDBUS_LIST_NAMES | ++ KDBUS_LIST_ACTIVATORS | ++ KDBUS_LIST_QUEUED, ++ .argv = argv, ++ .argc = ARRAY_SIZE(argv), ++ }; ++ ++ ret = kdbus_args_parse(&args, argp, &cmd); ++ if (ret != 0) ++ return ret; ++ ++ /* lock order: domain -> bus -> ep -> names -> conn */ ++ down_read(®->rwlock); ++ down_read(&conn->ep->bus->conn_rwlock); ++ down_read(&conn->ep->policy_db.entries_rwlock); ++ ++ /* size of records */ ++ size = 0; ++ ret = kdbus_list_all(conn, cmd->flags, NULL, &size, false); ++ if (ret < 0) ++ goto exit_unlock; ++ ++ if (size == 0) { ++ kdbus_pool_publish_empty(conn->pool, &cmd->offset, ++ &cmd->list_size); ++ } else { ++ slice = kdbus_pool_slice_alloc(conn->pool, size, false); ++ if (IS_ERR(slice)) { ++ ret = PTR_ERR(slice); ++ slice = NULL; ++ goto exit_unlock; ++ } ++ ++ /* copy the records */ ++ pos = 0; ++ ret = kdbus_list_all(conn, cmd->flags, slice, &pos, true); ++ if (ret < 0) ++ goto exit_unlock; ++ ++ WARN_ON(pos != size); ++ kdbus_pool_slice_publish(slice, &cmd->offset, &cmd->list_size); ++ } ++ ++ if (kdbus_member_set_user(&cmd->offset, argp, typeof(*cmd), offset) || ++ kdbus_member_set_user(&cmd->list_size, argp, ++ typeof(*cmd), list_size)) ++ ret = -EFAULT; ++ ++exit_unlock: ++ up_read(&conn->ep->policy_db.entries_rwlock); ++ up_read(&conn->ep->bus->conn_rwlock); ++ up_read(®->rwlock); ++ kdbus_pool_slice_release(slice); ++ return kdbus_args_clear(&args, ret); ++} +diff --git a/ipc/kdbus/names.h b/ipc/kdbus/names.h +new file mode 100644 +index 000000000000..3dd2589293e0 +--- /dev/null ++++ b/ipc/kdbus/names.h +@@ -0,0 +1,74 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> ++ * ++ * 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_id: Sequence number of name entry to be able to uniquely ++ * identify a name over its registration lifetime ++ * @flags: KDBUS_NAME_* flags ++ * @conn: Connection owning the name ++ * @activator: Connection of the activator queuing incoming messages ++ * @queue: List of queued connections ++ * @conn_entry: Entry in connection ++ * @hentry: Entry in registry map ++ * @name: The well-known name ++ */ ++struct kdbus_name_entry { ++ u64 name_id; ++ u64 flags; ++ struct kdbus_conn *conn; ++ struct kdbus_conn *activator; ++ struct list_head queue; ++ struct list_head conn_entry; ++ struct hlist_node hentry; ++ char name[]; ++}; ++ ++bool kdbus_name_is_valid(const char *p, bool allow_wildcard); ++ ++struct kdbus_name_registry *kdbus_name_registry_new(void); ++void kdbus_name_registry_free(struct kdbus_name_registry *reg); ++ ++struct kdbus_name_entry * ++kdbus_name_lookup_unlocked(struct kdbus_name_registry *reg, const char *name); ++ ++int kdbus_name_acquire(struct kdbus_name_registry *reg, ++ struct kdbus_conn *conn, const char *name, ++ u64 flags, u64 *return_flags); ++void kdbus_name_release_all(struct kdbus_name_registry *reg, ++ struct kdbus_conn *conn); ++ ++int kdbus_cmd_name_acquire(struct kdbus_conn *conn, void __user *argp); ++int kdbus_cmd_name_release(struct kdbus_conn *conn, void __user *argp); ++int kdbus_cmd_list(struct kdbus_conn *conn, void __user *argp); ++ ++#endif diff --git a/kdbus-add-node-and-filesystem-implementation.patch b/kdbus-add-node-and-filesystem-implementation.patch new file mode 100644 index 000000000..8259afb44 --- /dev/null +++ b/kdbus-add-node-and-filesystem-implementation.patch @@ -0,0 +1,1602 @@ +From: Daniel Mack <daniel@zonque.org> +Date: Fri, 14 Nov 2014 09:59:08 +0100 +Subject: [PATCH] kdbus: add node and filesystem implementation + +kdbusfs is a filesystem that will expose a fresh kdbus domain context +each time it is mounted. Per mount point, there will be a 'control' +node, which can be used to create buses. fs.c contains the +implementation of that pseudo-fs. Exported inodes of 'file' type have +their i_fop set to either kdbus_handle_control_ops or +kdbus_handle_ep_ops, depending on their type. The actual dispatching +of file operations is done from handle.c + +node.c is an implementation of a kdbus object that has an id and +children, organized in an R/B tree. The tree is used by the filesystem +code for lookup and iterator functions, and to deactivate children +once the parent is deactivated. Every inode exported by kdbusfs is +backed by a kdbus_node, hence it is embedded in struct kdbus_ep, +struct kdbus_bus and struct kdbus_domain. + +Signed-off-by: Daniel Mack <daniel@zonque.org> +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Djalal Harouni <tixxdz@opendz.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + include/uapi/linux/magic.h | 2 + + ipc/kdbus/fs.c | 510 +++++++++++++++++++++++++ + ipc/kdbus/fs.h | 28 ++ + ipc/kdbus/node.c | 910 +++++++++++++++++++++++++++++++++++++++++++++ + ipc/kdbus/node.h | 84 +++++ + 5 files changed, 1534 insertions(+) + create mode 100644 ipc/kdbus/fs.c + create mode 100644 ipc/kdbus/fs.h + create mode 100644 ipc/kdbus/node.c + create mode 100644 ipc/kdbus/node.h + +diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h +index 7b1425a6b370..ce2ac5a06b1b 100644 +--- a/include/uapi/linux/magic.h ++++ b/include/uapi/linux/magic.h +@@ -76,4 +76,6 @@ + #define BTRFS_TEST_MAGIC 0x73727279 + #define NSFS_MAGIC 0x6e736673 + ++#define KDBUS_SUPER_MAGIC 0x44427573 ++ + #endif /* __LINUX_MAGIC_H__ */ +diff --git a/ipc/kdbus/fs.c b/ipc/kdbus/fs.c +new file mode 100644 +index 000000000000..d01f33baaa0d +--- /dev/null ++++ b/ipc/kdbus/fs.c +@@ -0,0 +1,510 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#include <linux/dcache.h> ++#include <linux/fs.h> ++#include <linux/fsnotify.h> ++#include <linux/init.h> ++#include <linux/ipc_namespace.h> ++#include <linux/magic.h> ++#include <linux/module.h> ++#include <linux/mount.h> ++#include <linux/mutex.h> ++#include <linux/namei.h> ++#include <linux/pagemap.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++ ++#include "bus.h" ++#include "domain.h" ++#include "endpoint.h" ++#include "fs.h" ++#include "handle.h" ++#include "node.h" ++ ++#define kdbus_node_from_dentry(_dentry) \ ++ ((struct kdbus_node *)(_dentry)->d_fsdata) ++ ++static struct inode *fs_inode_get(struct super_block *sb, ++ struct kdbus_node *node); ++ ++/* ++ * Directory Management ++ */ ++ ++static inline unsigned char kdbus_dt_type(struct kdbus_node *node) ++{ ++ switch (node->type) { ++ case KDBUS_NODE_DOMAIN: ++ case KDBUS_NODE_BUS: ++ return DT_DIR; ++ case KDBUS_NODE_CONTROL: ++ case KDBUS_NODE_ENDPOINT: ++ return DT_REG; ++ } ++ ++ return DT_UNKNOWN; ++} ++ ++static int fs_dir_fop_iterate(struct file *file, struct dir_context *ctx) ++{ ++ struct dentry *dentry = file->f_path.dentry; ++ struct kdbus_node *parent = kdbus_node_from_dentry(dentry); ++ struct kdbus_node *old, *next = file->private_data; ++ ++ /* ++ * kdbusfs directory iterator (modelled after sysfs/kernfs) ++ * When iterating kdbusfs directories, we iterate all children of the ++ * parent kdbus_node object. We use ctx->pos to store the hash of the ++ * child and file->private_data to store a reference to the next node ++ * object. If ctx->pos is not modified via llseek while you iterate a ++ * directory, then we use the file->private_data node pointer to ++ * directly access the next node in the tree. ++ * However, if you directly seek on the directory, we have to find the ++ * closest node to that position and cannot use our node pointer. This ++ * means iterating the rb-tree to find the closest match and start over ++ * from there. ++ * Note that hash values are not neccessarily unique. Therefore, llseek ++ * is not guaranteed to seek to the same node that you got when you ++ * retrieved the position. Seeking to 0, 1, 2 and >=INT_MAX is safe, ++ * though. We could use the inode-number as position, but this would ++ * require another rb-tree for fast access. Kernfs and others already ++ * ignore those conflicts, so we should be fine, too. ++ */ ++ ++ if (!dir_emit_dots(file, ctx)) ++ return 0; ++ ++ /* acquire @next; if deactivated, or seek detected, find next node */ ++ old = next; ++ if (next && ctx->pos == next->hash) { ++ if (kdbus_node_acquire(next)) ++ kdbus_node_ref(next); ++ else ++ next = kdbus_node_next_child(parent, next); ++ } else { ++ next = kdbus_node_find_closest(parent, ctx->pos); ++ } ++ kdbus_node_unref(old); ++ ++ while (next) { ++ /* emit @next */ ++ file->private_data = next; ++ ctx->pos = next->hash; ++ ++ kdbus_node_release(next); ++ ++ if (!dir_emit(ctx, next->name, strlen(next->name), next->id, ++ kdbus_dt_type(next))) ++ return 0; ++ ++ /* find next node after @next */ ++ old = next; ++ next = kdbus_node_next_child(parent, next); ++ kdbus_node_unref(old); ++ } ++ ++ file->private_data = NULL; ++ ctx->pos = INT_MAX; ++ ++ return 0; ++} ++ ++static loff_t fs_dir_fop_llseek(struct file *file, loff_t offset, int whence) ++{ ++ struct inode *inode = file_inode(file); ++ loff_t ret; ++ ++ /* protect f_off against fop_iterate */ ++ mutex_lock(&inode->i_mutex); ++ ret = generic_file_llseek(file, offset, whence); ++ mutex_unlock(&inode->i_mutex); ++ ++ return ret; ++} ++ ++static int fs_dir_fop_release(struct inode *inode, struct file *file) ++{ ++ kdbus_node_unref(file->private_data); ++ return 0; ++} ++ ++static const struct file_operations fs_dir_fops = { ++ .read = generic_read_dir, ++ .iterate = fs_dir_fop_iterate, ++ .llseek = fs_dir_fop_llseek, ++ .release = fs_dir_fop_release, ++}; ++ ++static struct dentry *fs_dir_iop_lookup(struct inode *dir, ++ struct dentry *dentry, ++ unsigned int flags) ++{ ++ struct dentry *dnew = NULL; ++ struct kdbus_node *parent; ++ struct kdbus_node *node; ++ struct inode *inode; ++ ++ parent = kdbus_node_from_dentry(dentry->d_parent); ++ if (!kdbus_node_acquire(parent)) ++ return NULL; ++ ++ /* returns reference to _acquired_ child node */ ++ node = kdbus_node_find_child(parent, dentry->d_name.name); ++ if (node) { ++ dentry->d_fsdata = node; ++ inode = fs_inode_get(dir->i_sb, node); ++ if (IS_ERR(inode)) ++ dnew = ERR_CAST(inode); ++ else ++ dnew = d_splice_alias(inode, dentry); ++ ++ kdbus_node_release(node); ++ } ++ ++ kdbus_node_release(parent); ++ return dnew; ++} ++ ++static const struct inode_operations fs_dir_iops = { ++ .permission = generic_permission, ++ .lookup = fs_dir_iop_lookup, ++}; ++ ++/* ++ * Inode Management ++ */ ++ ++static const struct inode_operations fs_inode_iops = { ++ .permission = generic_permission, ++}; ++ ++static struct inode *fs_inode_get(struct super_block *sb, ++ struct kdbus_node *node) ++{ ++ struct inode *inode; ++ ++ inode = iget_locked(sb, node->id); ++ if (!inode) ++ return ERR_PTR(-ENOMEM); ++ if (!(inode->i_state & I_NEW)) ++ return inode; ++ ++ inode->i_private = kdbus_node_ref(node); ++ inode->i_mapping->a_ops = &empty_aops; ++ inode->i_mode = node->mode & S_IALLUGO; ++ inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME; ++ inode->i_uid = node->uid; ++ inode->i_gid = node->gid; ++ ++ switch (node->type) { ++ case KDBUS_NODE_DOMAIN: ++ case KDBUS_NODE_BUS: ++ inode->i_mode |= S_IFDIR; ++ inode->i_op = &fs_dir_iops; ++ inode->i_fop = &fs_dir_fops; ++ set_nlink(inode, 2); ++ break; ++ case KDBUS_NODE_CONTROL: ++ case KDBUS_NODE_ENDPOINT: ++ inode->i_mode |= S_IFREG; ++ inode->i_op = &fs_inode_iops; ++ inode->i_fop = &kdbus_handle_ops; ++ break; ++ } ++ ++ unlock_new_inode(inode); ++ ++ return inode; ++} ++ ++/* ++ * Superblock Management ++ */ ++ ++static int fs_super_dop_revalidate(struct dentry *dentry, unsigned int flags) ++{ ++ struct kdbus_node *node; ++ ++ /* Force lookup on negatives */ ++ if (!dentry->d_inode) ++ return 0; ++ ++ node = kdbus_node_from_dentry(dentry); ++ ++ /* see whether the node has been removed */ ++ if (!kdbus_node_is_active(node)) ++ return 0; ++ ++ return 1; ++} ++ ++static void fs_super_dop_release(struct dentry *dentry) ++{ ++ kdbus_node_unref(dentry->d_fsdata); ++} ++ ++static const struct dentry_operations fs_super_dops = { ++ .d_revalidate = fs_super_dop_revalidate, ++ .d_release = fs_super_dop_release, ++}; ++ ++static void fs_super_sop_evict_inode(struct inode *inode) ++{ ++ struct kdbus_node *node = kdbus_node_from_inode(inode); ++ ++ truncate_inode_pages_final(&inode->i_data); ++ clear_inode(inode); ++ kdbus_node_unref(node); ++} ++ ++static const struct super_operations fs_super_sops = { ++ .statfs = simple_statfs, ++ .drop_inode = generic_delete_inode, ++ .evict_inode = fs_super_sop_evict_inode, ++}; ++ ++static int fs_super_fill(struct super_block *sb) ++{ ++ struct kdbus_domain *domain = sb->s_fs_info; ++ struct inode *inode; ++ int ret; ++ ++ sb->s_blocksize = PAGE_CACHE_SIZE; ++ sb->s_blocksize_bits = PAGE_CACHE_SHIFT; ++ sb->s_magic = KDBUS_SUPER_MAGIC; ++ sb->s_maxbytes = MAX_LFS_FILESIZE; ++ sb->s_op = &fs_super_sops; ++ sb->s_time_gran = 1; ++ ++ inode = fs_inode_get(sb, &domain->node); ++ if (IS_ERR(inode)) ++ return PTR_ERR(inode); ++ ++ sb->s_root = d_make_root(inode); ++ if (!sb->s_root) { ++ /* d_make_root iput()s the inode on failure */ ++ return -ENOMEM; ++ } ++ ++ /* sb holds domain reference */ ++ sb->s_root->d_fsdata = &domain->node; ++ sb->s_d_op = &fs_super_dops; ++ ++ /* sb holds root reference */ ++ domain->dentry = sb->s_root; ++ ++ if (!kdbus_node_activate(&domain->node)) ++ return -ESHUTDOWN; ++ ++ ret = kdbus_domain_populate(domain, KDBUS_MAKE_ACCESS_WORLD); ++ if (ret < 0) ++ return ret; ++ ++ sb->s_flags |= MS_ACTIVE; ++ return 0; ++} ++ ++static void fs_super_kill(struct super_block *sb) ++{ ++ struct kdbus_domain *domain = sb->s_fs_info; ++ ++ if (domain) { ++ kdbus_node_deactivate(&domain->node); ++ domain->dentry = NULL; ++ } ++ ++ kill_anon_super(sb); ++ ++ if (domain) ++ kdbus_domain_unref(domain); ++} ++ ++static int fs_super_set(struct super_block *sb, void *data) ++{ ++ int ret; ++ ++ ret = set_anon_super(sb, data); ++ if (!ret) ++ sb->s_fs_info = data; ++ ++ return ret; ++} ++ ++static struct dentry *fs_super_mount(struct file_system_type *fs_type, ++ int flags, const char *dev_name, ++ void *data) ++{ ++ struct kdbus_domain *domain; ++ struct super_block *sb; ++ int ret; ++ ++ domain = kdbus_domain_new(KDBUS_MAKE_ACCESS_WORLD); ++ if (IS_ERR(domain)) ++ return ERR_CAST(domain); ++ ++ sb = sget(fs_type, NULL, fs_super_set, flags, domain); ++ if (IS_ERR(sb)) { ++ kdbus_node_deactivate(&domain->node); ++ kdbus_domain_unref(domain); ++ return ERR_CAST(sb); ++ } ++ ++ WARN_ON(sb->s_fs_info != domain); ++ WARN_ON(sb->s_root); ++ ++ ret = fs_super_fill(sb); ++ if (ret < 0) { ++ /* calls into ->kill_sb() when done */ ++ deactivate_locked_super(sb); ++ return ERR_PTR(ret); ++ } ++ ++ return dget(sb->s_root); ++} ++ ++static struct file_system_type fs_type = { ++ .name = KBUILD_MODNAME "fs", ++ .owner = THIS_MODULE, ++ .mount = fs_super_mount, ++ .kill_sb = fs_super_kill, ++ .fs_flags = FS_USERNS_MOUNT, ++}; ++ ++/** ++ * kdbus_fs_init() - register kdbus filesystem ++ * ++ * This registers a filesystem with the VFS layer. The filesystem is called ++ * `KBUILD_MODNAME "fs"', which usually resolves to `kdbusfs'. The nameing ++ * scheme allows to set KBUILD_MODNAME to "kdbus2" and you will get an ++ * independent filesystem for developers. ++ * ++ * Each mount of the kdbusfs filesystem has an kdbus_domain attached. ++ * Operations on this mount will only affect the attached domain. On each mount ++ * a new domain is automatically created and used for this mount exclusively. ++ * If you want to share a domain across multiple mounts, you need to bind-mount ++ * it. ++ * ++ * Mounts of kdbusfs (with a different domain each) are unrelated to each other ++ * and will never have any effect on any domain but their own. ++ * ++ * Return: 0 on success, negative error otherwise. ++ */ ++int kdbus_fs_init(void) ++{ ++ return register_filesystem(&fs_type); ++} ++ ++/** ++ * kdbus_fs_exit() - unregister kdbus filesystem ++ * ++ * This does the reverse to kdbus_fs_init(). It unregisters the kdbusfs ++ * filesystem from VFS and cleans up any allocated resources. ++ */ ++void kdbus_fs_exit(void) ++{ ++ unregister_filesystem(&fs_type); ++} ++ ++/* acquire domain of @node, making sure all ancestors are active */ ++static struct kdbus_domain *fs_acquire_domain(struct kdbus_node *node) ++{ ++ struct kdbus_domain *domain; ++ struct kdbus_node *iter; ++ ++ /* caller must guarantee that @node is linked */ ++ for (iter = node; iter->parent; iter = iter->parent) ++ if (!kdbus_node_is_active(iter->parent)) ++ return NULL; ++ ++ /* root nodes are always domains */ ++ if (WARN_ON(iter->type != KDBUS_NODE_DOMAIN)) ++ return NULL; ++ ++ domain = kdbus_domain_from_node(iter); ++ if (!kdbus_node_acquire(&domain->node)) ++ return NULL; ++ ++ return domain; ++} ++ ++/** ++ * kdbus_fs_flush() - flush dcache entries of a node ++ * @node: Node to flush entries of ++ * ++ * This flushes all VFS filesystem cache entries for a node and all its ++ * children. This should be called whenever a node is destroyed during ++ * runtime. It will flush the cache entries so the linked objects can be ++ * deallocated. ++ * ++ * This is a no-op if you call it on active nodes (they really should stay in ++ * cache) or on nodes with deactivated parents (flushing the parent is enough). ++ * Furthermore, there is no need to call it on nodes whose lifetime is bound to ++ * their parents'. In those cases, the parent-flush will always also flush the ++ * children. ++ */ ++void kdbus_fs_flush(struct kdbus_node *node) ++{ ++ struct dentry *dentry, *parent_dentry = NULL; ++ struct kdbus_domain *domain; ++ struct qstr name; ++ ++ /* active nodes should remain in cache */ ++ if (!kdbus_node_is_deactivated(node)) ++ return; ++ ++ /* nodes that were never linked were never instantiated */ ++ if (!node->parent) ++ return; ++ ++ /* acquire domain and verify all ancestors are active */ ++ domain = fs_acquire_domain(node); ++ if (!domain) ++ return; ++ ++ switch (node->type) { ++ case KDBUS_NODE_ENDPOINT: ++ if (WARN_ON(!node->parent || !node->parent->name)) ++ goto exit; ++ ++ name.name = node->parent->name; ++ name.len = strlen(node->parent->name); ++ parent_dentry = d_hash_and_lookup(domain->dentry, &name); ++ if (IS_ERR_OR_NULL(parent_dentry)) ++ goto exit; ++ ++ /* fallthrough */ ++ case KDBUS_NODE_BUS: ++ if (WARN_ON(!node->name)) ++ goto exit; ++ ++ name.name = node->name; ++ name.len = strlen(node->name); ++ dentry = d_hash_and_lookup(parent_dentry ? : domain->dentry, ++ &name); ++ if (!IS_ERR_OR_NULL(dentry)) { ++ d_invalidate(dentry); ++ dput(dentry); ++ } ++ ++ dput(parent_dentry); ++ break; ++ ++ default: ++ /* all other types are bound to their parent lifetime */ ++ break; ++ } ++ ++exit: ++ kdbus_node_release(&domain->node); ++} +diff --git a/ipc/kdbus/fs.h b/ipc/kdbus/fs.h +new file mode 100644 +index 000000000000..62f7d6abf11e +--- /dev/null ++++ b/ipc/kdbus/fs.h +@@ -0,0 +1,28 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#ifndef __KDBUSFS_H ++#define __KDBUSFS_H ++ ++#include <linux/kernel.h> ++ ++struct kdbus_node; ++ ++int kdbus_fs_init(void); ++void kdbus_fs_exit(void); ++void kdbus_fs_flush(struct kdbus_node *node); ++ ++#define kdbus_node_from_inode(_inode) \ ++ ((struct kdbus_node *)(_inode)->i_private) ++ ++#endif +diff --git a/ipc/kdbus/node.c b/ipc/kdbus/node.c +new file mode 100644 +index 000000000000..520df00e676a +--- /dev/null ++++ b/ipc/kdbus/node.c +@@ -0,0 +1,910 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#include <linux/atomic.h> ++#include <linux/fs.h> ++#include <linux/idr.h> ++#include <linux/kdev_t.h> ++#include <linux/rbtree.h> ++#include <linux/rwsem.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/wait.h> ++ ++#include "bus.h" ++#include "domain.h" ++#include "endpoint.h" ++#include "fs.h" ++#include "handle.h" ++#include "node.h" ++#include "util.h" ++ ++/** ++ * DOC: kdbus nodes ++ * ++ * Nodes unify lifetime management across exposed kdbus objects and provide a ++ * hierarchy. Each kdbus object, that might be exposed to user-space, has a ++ * kdbus_node object embedded and is linked into the hierarchy. Each node can ++ * have any number (0-n) of child nodes linked. Each child retains a reference ++ * to its parent node. For root-nodes, the parent is NULL. ++ * ++ * Each node object goes through a bunch of states during it's lifetime: ++ * * NEW ++ * * LINKED (can be skipped by NEW->FREED transition) ++ * * ACTIVE (can be skipped by LINKED->INACTIVE transition) ++ * * INACTIVE ++ * * DRAINED ++ * * FREED ++ * ++ * Each node is allocated by the caller and initialized via kdbus_node_init(). ++ * This never fails and sets the object into state NEW. From now on, ref-counts ++ * on the node manage its lifetime. During init, the ref-count is set to 1. Once ++ * it drops to 0, the node goes to state FREED and the node->free_cb() callback ++ * is called to deallocate any memory. ++ * ++ * After initializing a node, you usually link it into the hierarchy. You need ++ * to provide a parent node and a name. The node will be linked as child to the ++ * parent and a globally unique ID is assigned to the child. The name of the ++ * child must be unique for all children of this parent. Otherwise, linking the ++ * child will fail with -EEXIST. ++ * Note that the child is not marked active, yet. Admittedly, it prevents any ++ * other node from being linked with the same name (thus, it reserves that ++ * name), but any child-lookup (via name or unique ID) will never return this ++ * child unless it has been marked active. ++ * ++ * Once successfully linked, you can use kdbus_node_activate() to activate a ++ * child. This will mark the child active. This state can be skipped by directly ++ * deactivating the child via kdbus_node_deactivate() (see below). ++ * By activating a child, you enable any lookups on this child to succeed from ++ * now on. Furthermore, any code that got its hands on a reference to the node, ++ * can from now on "acquire" the node. ++ * ++ * Active References (or: 'acquiring' and 'releasing' a node) ++ * Additionally to normal object references, nodes support something we call ++ * "active references". An active reference can be acquired via ++ * kdbus_node_acquire() and released via kdbus_node_release(). A caller ++ * _must_ own a normal object reference whenever calling those functions. ++ * Unlike object references, acquiring an active reference can fail (by ++ * returning 'false' from kdbus_node_acquire()). An active reference can ++ * only be acquired if the node is marked active. If it is not marked ++ * active, yet, or if it was already deactivated, no more active references ++ * can be acquired, ever! ++ * Active references are used to track tasks working on a node. Whenever a ++ * task enters kernel-space to perform an action on a node, it acquires an ++ * active reference, performs the action and releases the reference again. ++ * While holding an active reference, the node is guaranteed to stay active. ++ * If the node is deactivated in parallel, the node is marked as ++ * deactivated, then we wait for all active references to be dropped, before ++ * we finally proceed with any cleanups. That is, if you hold an active ++ * reference to a node, any resources that are bound to the "active" state ++ * are guaranteed to stay accessible until you release your reference. ++ * ++ * Active-references are very similar to rw-locks, where acquiring a node is ++ * equal to try-read-lock and releasing to read-unlock. Deactivating a node ++ * means write-lock and never releasing it again. ++ * Unlike rw-locks, the 'active reference' concept is more versatile and ++ * avoids unusual rw-lock usage (never releasing a write-lock..). ++ * ++ * It is safe to acquire multiple active-references recursively. But you ++ * need to check the return value of kdbus_node_acquire() on _each_ call. It ++ * may stop granting references at _any_ time. ++ * ++ * You're free to perform any operations you want while holding an active ++ * reference, except sleeping for an indefinite period. Sleeping for a fixed ++ * amount of time is fine, but you usually should not wait on wait-queues ++ * without a timeout. ++ * For example, if you wait for I/O to happen, you should gather all data ++ * and schedule the I/O operation, then release your active reference and ++ * wait for it to complete. Then try to acquire a new reference. If it ++ * fails, perform any cleanup (the node is now dead). Otherwise, you can ++ * finish your operation. ++ * ++ * All nodes can be deactivated via kdbus_node_deactivate() at any time. You can ++ * call this multiple times, even in parallel or on nodes that were never ++ * linked, and it will just work. The only restriction is, you must not hold an ++ * active reference when calling kdbus_node_deactivate(). ++ * By deactivating a node, it is immediately marked inactive. Then, we wait for ++ * all active references to be released (called 'draining' the node). This ++ * shouldn't take very long as we don't perform long-lasting operations while ++ * holding an active reference. Note that once the node is marked inactive, no ++ * new active references can be acquired. ++ * Once all active references are dropped, the node is considered 'drained'. Now ++ * kdbus_node_deactivate() is called on each child of the node before we ++ * continue deactvating our node. That is, once all children are entirely ++ * deactivated, we call ->release_cb() of our node. ->release_cb() can release ++ * any resources on that node which are bound to the "active" state of a node. ++ * When done, we unlink the node from its parent rb-tree, mark it as ++ * 'released' and return. ++ * If kdbus_node_deactivate() is called multiple times (even in parallel), all ++ * but one caller will just wait until the node is fully deactivated. That is, ++ * one random caller of kdbus_node_deactivate() is selected to call ++ * ->release_cb() and cleanup the node. Only once all this is done, all other ++ * callers will return from kdbus_node_deactivate(). That is, it doesn't matter ++ * whether you're the selected caller or not, it will only return after ++ * everything is fully done. ++ * ++ * When a node is activated, we acquire a normal object reference to the node. ++ * This reference is dropped after deactivation is fully done (and only iff the ++ * node really was activated). This allows callers to link+activate a child node ++ * and then drop all refs. The node will be deactivated together with the ++ * parent, and then be freed when this reference is dropped. ++ * ++ * Currently, nodes provide a bunch of resources that external code can use ++ * directly. This includes: ++ * ++ * * node->waitq: Each node has its own wait-queue that is used to manage ++ * the 'active' state. When a node is deactivated, we wait on ++ * this queue until all active refs are dropped. Analogously, ++ * when you release an active reference on a deactivated ++ * node, and the active ref-count drops to 0, we wake up a ++ * single thread on this queue. Furthermore, once the ++ * ->release_cb() callback finished, we wake up all waiters. ++ * The node-owner is free to re-use this wait-queue for other ++ * purposes. As node-management uses this queue only during ++ * deactivation, it is usually totally fine to re-use the ++ * queue for other, preferably low-overhead, use-cases. ++ * ++ * * node->type: This field defines the type of the owner of this node. It ++ * must be set during node initialization and must remain ++ * constant. The node management never looks at this value, ++ * but external users might use to gain access to the owner ++ * object of a node. ++ * It is totally up to the owner of the node to define what ++ * their type means. Usually it means you can access the ++ * parent structure via container_of(), as long as you hold an ++ * active reference to the node. ++ * ++ * * node->free_cb: callback after all references are dropped ++ * node->release_cb: callback during node deactivation ++ * These fields must be set by the node owner during ++ * node initialization. They must remain constant. If ++ * NULL, they're skipped. ++ * ++ * * node->mode: filesystem access modes ++ * node->uid: filesystem owner uid ++ * node->gid: filesystem owner gid ++ * These fields must be set by the node owner during node ++ * initialization. They must remain constant and may be ++ * accessed by other callers to properly initialize ++ * filesystem nodes. ++ * ++ * * node->id: This is an unsigned 32bit integer allocated by an IDR. It is ++ * always kept as small as possible during allocation and is ++ * globally unique across all nodes allocated by this module. 0 ++ * is reserved as "not assigned" and is the default. ++ * The ID is assigned during kdbus_node_link() and is kept until ++ * the object is freed. Thus, the ID surpasses the active ++ * lifetime of a node. As long as you hold an object reference ++ * to a node (and the node was linked once), the ID is valid and ++ * unique. ++ * ++ * * node->name: name of this node ++ * node->hash: 31bit hash-value of @name (range [2..INT_MAX-1]) ++ * These values follow the same lifetime rules as node->id. ++ * They're initialized when the node is linked and then remain ++ * constant until the last object reference is dropped. ++ * Unlike the id, the name is only unique across all siblings ++ * and only until the node is deactivated. Currently, the name ++ * is even unique if linked but not activated, yet. This might ++ * change in the future, though. Code should not rely on this. ++ * ++ * * node->lock: lock to protect node->children, node->rb, node->parent ++ * * node->parent: Reference to parent node. This is set during LINK time ++ * and is dropped during destruction. You must not access ++ * it unless you hold an active reference to the node or if ++ * you know the node is dead. ++ * * node->children: rb-tree of all linked children of this node. You must ++ * not access this directly, but use one of the iterator ++ * or lookup helpers. ++ */ ++ ++/* ++ * Bias values track states of "active references". They're all negative. If a ++ * node is active, its active-ref-counter is >=0 and tracks all active ++ * references. Once a node is deactivaed, we subtract NODE_BIAS. This means, the ++ * counter is now negative but still counts the active references. Once it drops ++ * to exactly NODE_BIAS, we know all active references were dropped. Exactly one ++ * thread will change it to NODE_RELEASE now, perform cleanup and then put it ++ * into NODE_DRAINED. Once drained, all other threads that tried deactivating ++ * the node will now be woken up (thus, they wait until the node is fully done). ++ * The initial state during node-setup is NODE_NEW. If a node is directly ++ * deactivated without having ever been active, it is put into ++ * NODE_RELEASE_DIRECT instead of NODE_BIAS. This tracks this one-bit state ++ * across node-deactivation. The task putting it into NODE_RELEASE now knows ++ * whether the node was active before or not. ++ * ++ * Some archs implement atomic_sub(v) with atomic_add(-v), so reserve INT_MIN ++ * to avoid overflows if multiplied by -1. ++ */ ++#define KDBUS_NODE_BIAS (INT_MIN + 5) ++#define KDBUS_NODE_RELEASE_DIRECT (KDBUS_NODE_BIAS - 1) ++#define KDBUS_NODE_RELEASE (KDBUS_NODE_BIAS - 2) ++#define KDBUS_NODE_DRAINED (KDBUS_NODE_BIAS - 3) ++#define KDBUS_NODE_NEW (KDBUS_NODE_BIAS - 4) ++ ++/* global unique ID mapping for kdbus nodes */ ++static DEFINE_IDR(kdbus_node_idr); ++static DECLARE_RWSEM(kdbus_node_idr_lock); ++ ++/** ++ * kdbus_node_name_hash() - hash a name ++ * @name: The string to hash ++ * ++ * This computes the hash of @name. It is guaranteed to be in the range ++ * [2..INT_MAX-1]. The values 1, 2 and INT_MAX are unused as they are reserved ++ * for the filesystem code. ++ * ++ * Return: hash value of the passed string ++ */ ++static unsigned int kdbus_node_name_hash(const char *name) ++{ ++ unsigned int hash; ++ ++ /* reserve hash numbers 0, 1 and >=INT_MAX for magic directories */ ++ hash = kdbus_strhash(name) & INT_MAX; ++ if (hash < 2) ++ hash += 2; ++ if (hash >= INT_MAX) ++ hash = INT_MAX - 1; ++ ++ return hash; ++} ++ ++/** ++ * kdbus_node_name_compare() - compare a name with a node's name ++ * @hash: hash of the string to compare the node with ++ * @name: name to compare the node with ++ * @node: node to compare the name with ++ * ++ * Return: 0 if @name and @hash exactly match the information in @node, or ++ * an integer less than or greater than zero if @name is found, respectively, ++ * to be less than or be greater than the string stored in @node. ++ */ ++static int kdbus_node_name_compare(unsigned int hash, const char *name, ++ const struct kdbus_node *node) ++{ ++ if (hash != node->hash) ++ return hash - node->hash; ++ ++ return strcmp(name, node->name); ++} ++ ++/** ++ * kdbus_node_init() - initialize a kdbus_node ++ * @node: Pointer to the node to initialize ++ * @type: The type the node will have (KDBUS_NODE_*) ++ * ++ * The caller is responsible of allocating @node and initializating it to zero. ++ * Once this call returns, you must use the node_ref() and node_unref() ++ * functions to manage this node. ++ */ ++void kdbus_node_init(struct kdbus_node *node, unsigned int type) ++{ ++ atomic_set(&node->refcnt, 1); ++ mutex_init(&node->lock); ++ node->id = 0; ++ node->type = type; ++ RB_CLEAR_NODE(&node->rb); ++ node->children = RB_ROOT; ++ init_waitqueue_head(&node->waitq); ++ atomic_set(&node->active, KDBUS_NODE_NEW); ++} ++ ++/** ++ * kdbus_node_link() - link a node into the nodes system ++ * @node: Pointer to the node to initialize ++ * @parent: Pointer to a parent node, may be %NULL ++ * @name: The name of the node (or NULL if root node) ++ * ++ * This links a node into the hierarchy. This must not be called multiple times. ++ * If @parent is NULL, the node becomes a new root node. ++ * ++ * This call will fail if @name is not unique across all its siblings or if no ++ * ID could be allocated. You must not activate a node if linking failed! It is ++ * safe to deactivate it, though. ++ * ++ * Once you linked a node, you must call kdbus_node_deactivate() before you drop ++ * the last reference (even if you never activate the node). ++ * ++ * Return: 0 on success. negative error otherwise. ++ */ ++int kdbus_node_link(struct kdbus_node *node, struct kdbus_node *parent, ++ const char *name) ++{ ++ int ret; ++ ++ if (WARN_ON(node->type != KDBUS_NODE_DOMAIN && !parent)) ++ return -EINVAL; ++ ++ if (WARN_ON(parent && !name)) ++ return -EINVAL; ++ ++ if (name) { ++ node->name = kstrdup(name, GFP_KERNEL); ++ if (!node->name) ++ return -ENOMEM; ++ ++ node->hash = kdbus_node_name_hash(name); ++ } ++ ++ down_write(&kdbus_node_idr_lock); ++ ret = idr_alloc(&kdbus_node_idr, node, 1, 0, GFP_KERNEL); ++ if (ret >= 0) ++ node->id = ret; ++ up_write(&kdbus_node_idr_lock); ++ ++ if (ret < 0) ++ return ret; ++ ++ ret = 0; ++ ++ if (parent) { ++ struct rb_node **n, *prev; ++ ++ if (!kdbus_node_acquire(parent)) ++ return -ESHUTDOWN; ++ ++ mutex_lock(&parent->lock); ++ ++ n = &parent->children.rb_node; ++ prev = NULL; ++ ++ while (*n) { ++ struct kdbus_node *pos; ++ int result; ++ ++ pos = kdbus_node_from_rb(*n); ++ prev = *n; ++ result = kdbus_node_name_compare(node->hash, ++ node->name, ++ pos); ++ if (result == 0) { ++ ret = -EEXIST; ++ goto exit_unlock; ++ } ++ ++ if (result < 0) ++ n = &pos->rb.rb_left; ++ else ++ n = &pos->rb.rb_right; ++ } ++ ++ /* add new node and rebalance the tree */ ++ rb_link_node(&node->rb, prev, n); ++ rb_insert_color(&node->rb, &parent->children); ++ node->parent = kdbus_node_ref(parent); ++ ++exit_unlock: ++ mutex_unlock(&parent->lock); ++ kdbus_node_release(parent); ++ } ++ ++ return ret; ++} ++ ++/** ++ * kdbus_node_ref() - Acquire object reference ++ * @node: node to acquire reference to (or NULL) ++ * ++ * This acquires a new reference to @node. You must already own a reference when ++ * calling this! ++ * If @node is NULL, this is a no-op. ++ * ++ * Return: @node is returned ++ */ ++struct kdbus_node *kdbus_node_ref(struct kdbus_node *node) ++{ ++ if (node) ++ atomic_inc(&node->refcnt); ++ return node; ++} ++ ++/** ++ * kdbus_node_unref() - Drop object reference ++ * @node: node to drop reference to (or NULL) ++ * ++ * This drops an object reference to @node. You must not access the node if you ++ * no longer own a reference. ++ * If the ref-count drops to 0, the object will be destroyed (->free_cb will be ++ * called). ++ * ++ * If you linked or activated the node, you must deactivate the node before you ++ * drop your last reference! If you didn't link or activate the node, you can ++ * drop any reference you want. ++ * ++ * Note that this calls into ->free_cb() and thus _might_ sleep. The ->free_cb() ++ * callbacks must not acquire any outer locks, though. So you can safely drop ++ * references while holding locks. ++ * ++ * If @node is NULL, this is a no-op. ++ * ++ * Return: This always returns NULL ++ */ ++struct kdbus_node *kdbus_node_unref(struct kdbus_node *node) ++{ ++ if (node && atomic_dec_and_test(&node->refcnt)) { ++ struct kdbus_node safe = *node; ++ ++ WARN_ON(atomic_read(&node->active) != KDBUS_NODE_DRAINED); ++ WARN_ON(!RB_EMPTY_NODE(&node->rb)); ++ ++ if (node->free_cb) ++ node->free_cb(node); ++ ++ down_write(&kdbus_node_idr_lock); ++ if (safe.id > 0) ++ idr_remove(&kdbus_node_idr, safe.id); ++ /* drop caches after last node to not leak memory on unload */ ++ if (idr_is_empty(&kdbus_node_idr)) { ++ idr_destroy(&kdbus_node_idr); ++ idr_init(&kdbus_node_idr); ++ } ++ up_write(&kdbus_node_idr_lock); ++ ++ kfree(safe.name); ++ ++ /* ++ * kdbusfs relies on the parent to be available even after the ++ * node was deactivated and unlinked. Therefore, we pin it ++ * until a node is destroyed. ++ */ ++ kdbus_node_unref(safe.parent); ++ } ++ ++ return NULL; ++} ++ ++/** ++ * kdbus_node_is_active() - test whether a node is active ++ * @node: node to test ++ * ++ * This checks whether @node is active. That means, @node was linked and ++ * activated by the node owner and hasn't been deactivated, yet. If, and only ++ * if, a node is active, kdbus_node_acquire() will be able to acquire active ++ * references. ++ * ++ * Note that this function does not give any lifetime guarantees. After this ++ * call returns, the node might be deactivated immediately. Normally, what you ++ * want is to acquire a real active reference via kdbus_node_acquire(). ++ * ++ * Return: true if @node is active, false otherwise ++ */ ++bool kdbus_node_is_active(struct kdbus_node *node) ++{ ++ return atomic_read(&node->active) >= 0; ++} ++ ++/** ++ * kdbus_node_is_deactivated() - test whether a node was already deactivated ++ * @node: node to test ++ * ++ * This checks whether kdbus_node_deactivate() was called on @node. Note that ++ * this might be true even if you never deactivated the node directly, but only ++ * one of its ancestors. ++ * ++ * Note that even if this returns 'false', the node might get deactivated ++ * immediately after the call returns. ++ * ++ * Return: true if @node was already deactivated, false if not ++ */ ++bool kdbus_node_is_deactivated(struct kdbus_node *node) ++{ ++ int v; ++ ++ v = atomic_read(&node->active); ++ return v != KDBUS_NODE_NEW && v < 0; ++} ++ ++/** ++ * kdbus_node_activate() - activate a node ++ * @node: node to activate ++ * ++ * This marks @node as active if, and only if, the node wasn't activated nor ++ * deactivated, yet, and the parent is still active. Any but the first call to ++ * kdbus_node_activate() is a no-op. ++ * If you called kdbus_node_deactivate() before, then even the first call to ++ * kdbus_node_activate() will be a no-op. ++ * ++ * This call doesn't give any lifetime guarantees. The node might get ++ * deactivated immediately after this call returns. Or the parent might already ++ * be deactivated, which will make this call a no-op. ++ * ++ * If this call successfully activated a node, it will take an object reference ++ * to it. This reference is dropped after the node is deactivated. Therefore, ++ * the object owner can safely drop their reference to @node iff they know that ++ * its parent node will get deactivated at some point. Once the parent node is ++ * deactivated, it will deactivate all its child and thus drop this reference ++ * again. ++ * ++ * Return: True if this call successfully activated the node, otherwise false. ++ * Note that this might return false, even if the node is still active ++ * (eg., if you called this a second time). ++ */ ++bool kdbus_node_activate(struct kdbus_node *node) ++{ ++ bool res = false; ++ ++ mutex_lock(&node->lock); ++ if (atomic_read(&node->active) == KDBUS_NODE_NEW) { ++ atomic_sub(KDBUS_NODE_NEW, &node->active); ++ /* activated nodes have ref +1 */ ++ kdbus_node_ref(node); ++ res = true; ++ } ++ mutex_unlock(&node->lock); ++ ++ return res; ++} ++ ++/** ++ * kdbus_node_deactivate() - deactivate a node ++ * @node: The node to deactivate. ++ * ++ * This function recursively deactivates this node and all its children. It ++ * returns only once all children and the node itself were recursively disabled ++ * (even if you call this function multiple times in parallel). ++ * ++ * It is safe to call this function on _any_ node that was initialized _any_ ++ * number of times. ++ * ++ * This call may sleep, as it waits for all active references to be dropped. ++ */ ++void kdbus_node_deactivate(struct kdbus_node *node) ++{ ++ struct kdbus_node *pos, *child; ++ struct rb_node *rb; ++ int v_pre, v_post; ++ ++ pos = node; ++ ++ /* ++ * To avoid recursion, we perform back-tracking while deactivating ++ * nodes. For each node we enter, we first mark the active-counter as ++ * deactivated by adding BIAS. If the node as children, we set the first ++ * child as current position and start over. If the node has no ++ * children, we drain the node by waiting for all active refs to be ++ * dropped and then releasing the node. ++ * ++ * After the node is released, we set its parent as current position ++ * and start over. If the current position was the initial node, we're ++ * done. ++ * ++ * Note that this function can be called in parallel by multiple ++ * callers. We make sure that each node is only released once, and any ++ * racing caller will wait until the other thread fully released that ++ * node. ++ */ ++ ++ for (;;) { ++ /* ++ * Add BIAS to node->active to mark it as inactive. If it was ++ * never active before, immediately mark it as RELEASE_INACTIVE ++ * so we remember this state. ++ * We cannot remember v_pre as we might iterate into the ++ * children, overwriting v_pre, before we can release our node. ++ */ ++ mutex_lock(&pos->lock); ++ v_pre = atomic_read(&pos->active); ++ if (v_pre >= 0) ++ atomic_add_return(KDBUS_NODE_BIAS, &pos->active); ++ else if (v_pre == KDBUS_NODE_NEW) ++ atomic_set(&pos->active, KDBUS_NODE_RELEASE_DIRECT); ++ mutex_unlock(&pos->lock); ++ ++ /* wait until all active references were dropped */ ++ wait_event(pos->waitq, ++ atomic_read(&pos->active) <= KDBUS_NODE_BIAS); ++ ++ mutex_lock(&pos->lock); ++ /* recurse into first child if any */ ++ rb = rb_first(&pos->children); ++ if (rb) { ++ child = kdbus_node_ref(kdbus_node_from_rb(rb)); ++ mutex_unlock(&pos->lock); ++ pos = child; ++ continue; ++ } ++ ++ /* mark object as RELEASE */ ++ v_post = atomic_read(&pos->active); ++ if (v_post == KDBUS_NODE_BIAS || ++ v_post == KDBUS_NODE_RELEASE_DIRECT) ++ atomic_set(&pos->active, KDBUS_NODE_RELEASE); ++ mutex_unlock(&pos->lock); ++ ++ /* ++ * If this is the thread that marked the object as RELEASE, we ++ * perform the actual release. Otherwise, we wait until the ++ * release is done and the node is marked as DRAINED. ++ */ ++ if (v_post == KDBUS_NODE_BIAS || ++ v_post == KDBUS_NODE_RELEASE_DIRECT) { ++ if (pos->release_cb) ++ pos->release_cb(pos, v_post == KDBUS_NODE_BIAS); ++ ++ if (pos->parent) { ++ mutex_lock(&pos->parent->lock); ++ if (!RB_EMPTY_NODE(&pos->rb)) { ++ rb_erase(&pos->rb, ++ &pos->parent->children); ++ RB_CLEAR_NODE(&pos->rb); ++ } ++ mutex_unlock(&pos->parent->lock); ++ } ++ ++ /* mark as DRAINED */ ++ atomic_set(&pos->active, KDBUS_NODE_DRAINED); ++ wake_up_all(&pos->waitq); ++ ++ /* drop VFS cache */ ++ kdbus_fs_flush(pos); ++ ++ /* ++ * If the node was activated and somone subtracted BIAS ++ * from it to deactivate it, we, and only us, are ++ * responsible to release the extra ref-count that was ++ * taken once in kdbus_node_activate(). ++ * If the node was never activated, no-one ever ++ * subtracted BIAS, but instead skipped that state and ++ * immediately went to NODE_RELEASE_DIRECT. In that case ++ * we must not drop the reference. ++ */ ++ if (v_post == KDBUS_NODE_BIAS) ++ kdbus_node_unref(pos); ++ } else { ++ /* wait until object is DRAINED */ ++ wait_event(pos->waitq, ++ atomic_read(&pos->active) == KDBUS_NODE_DRAINED); ++ } ++ ++ /* ++ * We're done with the current node. Continue on its parent ++ * again, which will try deactivating its next child, or itself ++ * if no child is left. ++ * If we've reached our initial node again, we are done and ++ * can safely return. ++ */ ++ if (pos == node) ++ break; ++ ++ child = pos; ++ pos = pos->parent; ++ kdbus_node_unref(child); ++ } ++} ++ ++/** ++ * kdbus_node_acquire() - Acquire an active ref on a node ++ * @node: The node ++ * ++ * This acquires an active-reference to @node. This will only succeed if the ++ * node is active. You must release this active reference via ++ * kdbus_node_release() again. ++ * ++ * See the introduction to "active references" for more details. ++ * ++ * Return: %true if @node was non-NULL and active ++ */ ++bool kdbus_node_acquire(struct kdbus_node *node) ++{ ++ return node && atomic_inc_unless_negative(&node->active); ++} ++ ++/** ++ * kdbus_node_release() - Release an active ref on a node ++ * @node: The node ++ * ++ * This releases an active reference that was previously acquired via ++ * kdbus_node_acquire(). See kdbus_node_acquire() for details. ++ */ ++void kdbus_node_release(struct kdbus_node *node) ++{ ++ if (node && atomic_dec_return(&node->active) == KDBUS_NODE_BIAS) ++ wake_up(&node->waitq); ++} ++ ++/** ++ * kdbus_node_find_child() - Find child by name ++ * @node: parent node to search through ++ * @name: name of child node ++ * ++ * This searches through all children of @node for a child-node with name @name. ++ * If not found, or if the child is deactivated, NULL is returned. Otherwise, ++ * the child is acquired and a new reference is returned. ++ * ++ * If you're done with the child, you need to release it and drop your ++ * reference. ++ * ++ * This function does not acquire the parent node. However, if the parent was ++ * already deactivated, then kdbus_node_deactivate() will, at some point, also ++ * deactivate the child. Therefore, we can rely on the explicit ordering during ++ * deactivation. ++ * ++ * Return: Reference to acquired child node, or NULL if not found / not active. ++ */ ++struct kdbus_node *kdbus_node_find_child(struct kdbus_node *node, ++ const char *name) ++{ ++ struct kdbus_node *child; ++ struct rb_node *rb; ++ unsigned int hash; ++ int ret; ++ ++ hash = kdbus_node_name_hash(name); ++ ++ mutex_lock(&node->lock); ++ rb = node->children.rb_node; ++ while (rb) { ++ child = kdbus_node_from_rb(rb); ++ ret = kdbus_node_name_compare(hash, name, child); ++ if (ret < 0) ++ rb = rb->rb_left; ++ else if (ret > 0) ++ rb = rb->rb_right; ++ else ++ break; ++ } ++ if (rb && kdbus_node_acquire(child)) ++ kdbus_node_ref(child); ++ else ++ child = NULL; ++ mutex_unlock(&node->lock); ++ ++ return child; ++} ++ ++static struct kdbus_node *node_find_closest_unlocked(struct kdbus_node *node, ++ unsigned int hash, ++ const char *name) ++{ ++ struct kdbus_node *n, *pos = NULL; ++ struct rb_node *rb; ++ int res; ++ ++ /* ++ * Find the closest child with ``node->hash >= hash'', or, if @name is ++ * valid, ``node->name >= name'' (where '>=' is the lex. order). ++ */ ++ ++ rb = node->children.rb_node; ++ while (rb) { ++ n = kdbus_node_from_rb(rb); ++ ++ if (name) ++ res = kdbus_node_name_compare(hash, name, n); ++ else ++ res = hash - n->hash; ++ ++ if (res <= 0) { ++ rb = rb->rb_left; ++ pos = n; ++ } else { /* ``hash > n->hash'', ``name > n->name'' */ ++ rb = rb->rb_right; ++ } ++ } ++ ++ return pos; ++} ++ ++/** ++ * kdbus_node_find_closest() - Find closest child-match ++ * @node: parent node to search through ++ * @hash: hash value to find closest match for ++ * ++ * Find the closest child of @node with a hash greater than or equal to @hash. ++ * The closest match is the left-most child of @node with this property. Which ++ * means, it is the first child with that hash returned by ++ * kdbus_node_next_child(), if you'd iterate the whole parent node. ++ * ++ * Return: Reference to acquired child, or NULL if none found. ++ */ ++struct kdbus_node *kdbus_node_find_closest(struct kdbus_node *node, ++ unsigned int hash) ++{ ++ struct kdbus_node *child; ++ struct rb_node *rb; ++ ++ mutex_lock(&node->lock); ++ ++ child = node_find_closest_unlocked(node, hash, NULL); ++ while (child && !kdbus_node_acquire(child)) { ++ rb = rb_next(&child->rb); ++ if (rb) ++ child = kdbus_node_from_rb(rb); ++ else ++ child = NULL; ++ } ++ kdbus_node_ref(child); ++ ++ mutex_unlock(&node->lock); ++ ++ return child; ++} ++ ++/** ++ * kdbus_node_next_child() - Acquire next child ++ * @node: parent node ++ * @prev: previous child-node position or NULL ++ * ++ * This function returns a reference to the next active child of @node, after ++ * the passed position @prev. If @prev is NULL, a reference to the first active ++ * child is returned. If no more active children are found, NULL is returned. ++ * ++ * This function acquires the next child it returns. If you're done with the ++ * returned pointer, you need to release _and_ unref it. ++ * ++ * The passed in pointer @prev is not modified by this function, and it does ++ * *not* have to be active. If @prev was acquired via different means, or if it ++ * was unlinked from its parent before you pass it in, then this iterator will ++ * still return the next active child (it will have to search through the ++ * rb-tree based on the node-name, though). ++ * However, @prev must not be linked to a different parent than @node! ++ * ++ * Return: Reference to next acquired child, or NULL if at the end. ++ */ ++struct kdbus_node *kdbus_node_next_child(struct kdbus_node *node, ++ struct kdbus_node *prev) ++{ ++ struct kdbus_node *pos = NULL; ++ struct rb_node *rb; ++ ++ mutex_lock(&node->lock); ++ ++ if (!prev) { ++ /* ++ * New iteration; find first node in rb-tree and try to acquire ++ * it. If we got it, directly return it as first element. ++ * Otherwise, the loop below will find the next active node. ++ */ ++ rb = rb_first(&node->children); ++ if (!rb) ++ goto exit; ++ pos = kdbus_node_from_rb(rb); ++ if (kdbus_node_acquire(pos)) ++ goto exit; ++ } else if (RB_EMPTY_NODE(&prev->rb)) { ++ /* ++ * The current iterator is no longer linked to the rb-tree. Use ++ * its hash value and name to find the next _higher_ node and ++ * acquire it. If we got it, return it as next element. ++ * Otherwise, the loop below will find the next active node. ++ */ ++ pos = node_find_closest_unlocked(node, prev->hash, prev->name); ++ if (!pos) ++ goto exit; ++ if (kdbus_node_acquire(pos)) ++ goto exit; ++ } else { ++ /* ++ * The current iterator is still linked to the parent. Set it ++ * as current position and use the loop below to find the next ++ * active element. ++ */ ++ pos = prev; ++ } ++ ++ /* @pos was already returned or is inactive; find next active node */ ++ do { ++ rb = rb_next(&pos->rb); ++ if (rb) ++ pos = kdbus_node_from_rb(rb); ++ else ++ pos = NULL; ++ } while (pos && !kdbus_node_acquire(pos)); ++ ++exit: ++ /* @pos is NULL or acquired. Take ref if non-NULL and return it */ ++ kdbus_node_ref(pos); ++ mutex_unlock(&node->lock); ++ return pos; ++} +diff --git a/ipc/kdbus/node.h b/ipc/kdbus/node.h +new file mode 100644 +index 000000000000..be125ce4fd58 +--- /dev/null ++++ b/ipc/kdbus/node.h +@@ -0,0 +1,84 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#ifndef __KDBUS_NODE_H ++#define __KDBUS_NODE_H ++ ++#include <linux/atomic.h> ++#include <linux/kernel.h> ++#include <linux/mutex.h> ++#include <linux/wait.h> ++ ++struct kdbus_node; ++ ++enum kdbus_node_type { ++ KDBUS_NODE_DOMAIN, ++ KDBUS_NODE_CONTROL, ++ KDBUS_NODE_BUS, ++ KDBUS_NODE_ENDPOINT, ++}; ++ ++typedef void (*kdbus_node_free_t) (struct kdbus_node *node); ++typedef void (*kdbus_node_release_t) (struct kdbus_node *node, bool was_active); ++ ++struct kdbus_node { ++ atomic_t refcnt; ++ atomic_t active; ++ wait_queue_head_t waitq; ++ ++ /* static members */ ++ unsigned int type; ++ kdbus_node_free_t free_cb; ++ kdbus_node_release_t release_cb; ++ umode_t mode; ++ kuid_t uid; ++ kgid_t gid; ++ ++ /* valid once linked */ ++ char *name; ++ unsigned int hash; ++ unsigned int id; ++ struct kdbus_node *parent; /* may be NULL */ ++ ++ /* valid iff active */ ++ struct mutex lock; ++ struct rb_node rb; ++ struct rb_root children; ++}; ++ ++#define kdbus_node_from_rb(_node) rb_entry((_node), struct kdbus_node, rb) ++ ++void kdbus_node_init(struct kdbus_node *node, unsigned int type); ++ ++int kdbus_node_link(struct kdbus_node *node, struct kdbus_node *parent, ++ const char *name); ++ ++struct kdbus_node *kdbus_node_ref(struct kdbus_node *node); ++struct kdbus_node *kdbus_node_unref(struct kdbus_node *node); ++ ++bool kdbus_node_is_active(struct kdbus_node *node); ++bool kdbus_node_is_deactivated(struct kdbus_node *node); ++bool kdbus_node_activate(struct kdbus_node *node); ++void kdbus_node_deactivate(struct kdbus_node *node); ++ ++bool kdbus_node_acquire(struct kdbus_node *node); ++void kdbus_node_release(struct kdbus_node *node); ++ ++struct kdbus_node *kdbus_node_find_child(struct kdbus_node *node, ++ const char *name); ++struct kdbus_node *kdbus_node_find_closest(struct kdbus_node *node, ++ unsigned int hash); ++struct kdbus_node *kdbus_node_next_child(struct kdbus_node *node, ++ struct kdbus_node *prev); ++ ++#endif diff --git a/kdbus-add-policy-database-implementation.patch b/kdbus-add-policy-database-implementation.patch new file mode 100644 index 000000000..4beba4b61 --- /dev/null +++ b/kdbus-add-policy-database-implementation.patch @@ -0,0 +1,585 @@ +From: Daniel Mack <daniel@zonque.org> +Date: Thu, 11 Sep 2014 19:00:43 +0200 +Subject: [PATCH] kdbus: add policy database implementation + +This patch adds the policy database implementation. + +A policy database restricts the possibilities of connections to own, +see and talk to well-known names. It can be associated with a bus +(through a policy holder connection) or a custom endpoint. + +By default, buses have an empty policy database that is augmented on +demand when a policy holder connection is instantiated. + +Policies are set through KDBUS_CMD_HELLO (when creating a policy +holder connection), KDBUS_CMD_CONN_UPDATE (when updating a policy +holder connection), KDBUS_CMD_EP_MAKE (creating a custom endpoint) +or KDBUS_CMD_EP_UPDATE (updating a custom endpoint). In all cases, +the name and policy access information is stored in items of type +KDBUS_ITEM_NAME and KDBUS_ITEM_POLICY_ACCESS. + +See kdbus.policy(7) for more details. + +Signed-off-by: Daniel Mack <daniel@zonque.org> +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Djalal Harouni <tixxdz@opendz.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + ipc/kdbus/policy.c | 489 +++++++++++++++++++++++++++++++++++++++++++++++++++++ + ipc/kdbus/policy.h | 51 ++++++ + 2 files changed, 540 insertions(+) + create mode 100644 ipc/kdbus/policy.c + create mode 100644 ipc/kdbus/policy.h + +diff --git a/ipc/kdbus/policy.c b/ipc/kdbus/policy.c +new file mode 100644 +index 000000000000..dd7fffaafa84 +--- /dev/null ++++ b/ipc/kdbus/policy.c +@@ -0,0 +1,489 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * Copyright (C) 2014-2015 Djalal Harouni ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#include <linux/dcache.h> ++#include <linux/fs.h> ++#include <linux/init.h> ++#include <linux/mutex.h> ++#include <linux/sched.h> ++#include <linux/sizes.h> ++#include <linux/slab.h> ++#include <linux/uaccess.h> ++ ++#include "bus.h" ++#include "connection.h" ++#include "domain.h" ++#include "item.h" ++#include "names.h" ++#include "policy.h" ++ ++#define KDBUS_POLICY_HASH_SIZE 64 ++ ++/** ++ * struct kdbus_policy_db_entry_access - a database entry access item ++ * @type: One of KDBUS_POLICY_ACCESS_* types ++ * @access: Access to grant. One of KDBUS_POLICY_* ++ * @uid: For KDBUS_POLICY_ACCESS_USER, the global uid ++ * @gid: For KDBUS_POLICY_ACCESS_GROUP, the global gid ++ * @list: List entry item for the entry's list ++ * ++ * This is the internal version of struct kdbus_policy_db_access. ++ */ ++struct kdbus_policy_db_entry_access { ++ u8 type; /* USER, GROUP, WORLD */ ++ u8 access; /* OWN, TALK, SEE */ ++ union { ++ kuid_t uid; /* global uid */ ++ kgid_t gid; /* global gid */ ++ }; ++ struct list_head list; ++}; ++ ++/** ++ * struct kdbus_policy_db_entry - a policy database entry ++ * @name: The name to match the policy entry against ++ * @hentry: The hash entry for the database's entries_hash ++ * @access_list: List head for keeping tracks of the entry's ++ * access items. ++ * @owner: The owner of this entry. Can be a kdbus_conn or ++ * a kdbus_ep object. ++ * @wildcard: The name is a wildcard, such as ending on '.*' ++ */ ++struct kdbus_policy_db_entry { ++ char *name; ++ struct hlist_node hentry; ++ struct list_head access_list; ++ const void *owner; ++ bool wildcard:1; ++}; ++ ++static void kdbus_policy_entry_free(struct kdbus_policy_db_entry *e) ++{ ++ struct kdbus_policy_db_entry_access *a, *tmp; ++ ++ list_for_each_entry_safe(a, tmp, &e->access_list, list) { ++ list_del(&a->list); ++ kfree(a); ++ } ++ ++ kfree(e->name); ++ kfree(e); ++} ++ ++static unsigned int kdbus_strnhash(const char *str, size_t len) ++{ ++ unsigned long hash = init_name_hash(); ++ ++ while (len--) ++ hash = partial_name_hash(*str++, hash); ++ ++ return end_name_hash(hash); ++} ++ ++static const struct kdbus_policy_db_entry * ++kdbus_policy_lookup(struct kdbus_policy_db *db, const char *name, u32 hash) ++{ ++ struct kdbus_policy_db_entry *e; ++ const char *dot; ++ size_t len; ++ ++ /* find exact match */ ++ hash_for_each_possible(db->entries_hash, e, hentry, hash) ++ if (strcmp(e->name, name) == 0 && !e->wildcard) ++ return e; ++ ++ /* find wildcard match */ ++ ++ dot = strrchr(name, '.'); ++ if (!dot) ++ return NULL; ++ ++ len = dot - name; ++ hash = kdbus_strnhash(name, len); ++ ++ hash_for_each_possible(db->entries_hash, e, hentry, hash) ++ if (e->wildcard && !strncmp(e->name, name, len) && ++ !e->name[len]) ++ return e; ++ ++ return NULL; ++} ++ ++/** ++ * kdbus_policy_db_clear - release all memory from a policy db ++ * @db: The policy database ++ */ ++void kdbus_policy_db_clear(struct kdbus_policy_db *db) ++{ ++ struct kdbus_policy_db_entry *e; ++ struct hlist_node *tmp; ++ unsigned int i; ++ ++ /* purge entries */ ++ down_write(&db->entries_rwlock); ++ hash_for_each_safe(db->entries_hash, i, tmp, e, hentry) { ++ hash_del(&e->hentry); ++ kdbus_policy_entry_free(e); ++ } ++ up_write(&db->entries_rwlock); ++} ++ ++/** ++ * kdbus_policy_db_init() - initialize a new policy database ++ * @db: The location of the database ++ * ++ * This initializes a new policy-db. The underlying memory must have been ++ * cleared to zero by the caller. ++ */ ++void kdbus_policy_db_init(struct kdbus_policy_db *db) ++{ ++ hash_init(db->entries_hash); ++ init_rwsem(&db->entries_rwlock); ++} ++ ++/** ++ * kdbus_policy_query_unlocked() - Query the policy database ++ * @db: Policy database ++ * @cred: Credentials to test against ++ * @name: Name to query ++ * @hash: Hash value of @name ++ * ++ * Same as kdbus_policy_query() but requires the caller to lock the policy ++ * database against concurrent writes. ++ * ++ * Return: The highest KDBUS_POLICY_* access type found, or -EPERM if none. ++ */ ++int kdbus_policy_query_unlocked(struct kdbus_policy_db *db, ++ const struct cred *cred, const char *name, ++ unsigned int hash) ++{ ++ struct kdbus_policy_db_entry_access *a; ++ const struct kdbus_policy_db_entry *e; ++ int i, highest = -EPERM; ++ ++ e = kdbus_policy_lookup(db, name, hash); ++ if (!e) ++ return -EPERM; ++ ++ list_for_each_entry(a, &e->access_list, list) { ++ if ((int)a->access <= highest) ++ continue; ++ ++ switch (a->type) { ++ case KDBUS_POLICY_ACCESS_USER: ++ if (uid_eq(cred->euid, a->uid)) ++ highest = a->access; ++ break; ++ case KDBUS_POLICY_ACCESS_GROUP: ++ if (gid_eq(cred->egid, a->gid)) { ++ highest = a->access; ++ break; ++ } ++ ++ for (i = 0; i < cred->group_info->ngroups; i++) { ++ kgid_t gid = GROUP_AT(cred->group_info, i); ++ ++ if (gid_eq(gid, a->gid)) { ++ highest = a->access; ++ break; ++ } ++ } ++ ++ break; ++ case KDBUS_POLICY_ACCESS_WORLD: ++ highest = a->access; ++ break; ++ } ++ ++ /* OWN is the highest possible policy */ ++ if (highest >= KDBUS_POLICY_OWN) ++ break; ++ } ++ ++ return highest; ++} ++ ++/** ++ * kdbus_policy_query() - Query the policy database ++ * @db: Policy database ++ * @cred: Credentials to test against ++ * @name: Name to query ++ * @hash: Hash value of @name ++ * ++ * Query the policy database @db for the access rights of @cred to the name ++ * @name. The access rights of @cred are returned, or -EPERM if no access is ++ * granted. ++ * ++ * This call effectively searches for the highest access-right granted to ++ * @cred. The caller should really cache those as policy lookups are rather ++ * expensive. ++ * ++ * Return: The highest KDBUS_POLICY_* access type found, or -EPERM if none. ++ */ ++int kdbus_policy_query(struct kdbus_policy_db *db, const struct cred *cred, ++ const char *name, unsigned int hash) ++{ ++ int ret; ++ ++ down_read(&db->entries_rwlock); ++ ret = kdbus_policy_query_unlocked(db, cred, name, hash); ++ up_read(&db->entries_rwlock); ++ ++ return ret; ++} ++ ++static void __kdbus_policy_remove_owner(struct kdbus_policy_db *db, ++ const void *owner) ++{ ++ struct kdbus_policy_db_entry *e; ++ struct hlist_node *tmp; ++ int i; ++ ++ hash_for_each_safe(db->entries_hash, i, tmp, e, hentry) ++ if (e->owner == owner) { ++ hash_del(&e->hentry); ++ kdbus_policy_entry_free(e); ++ } ++} ++ ++/** ++ * kdbus_policy_remove_owner() - remove all entries related to a connection ++ * @db: The policy database ++ * @owner: The connection which items to remove ++ */ ++void kdbus_policy_remove_owner(struct kdbus_policy_db *db, ++ const void *owner) ++{ ++ down_write(&db->entries_rwlock); ++ __kdbus_policy_remove_owner(db, owner); ++ up_write(&db->entries_rwlock); ++} ++ ++/* ++ * Convert user provided policy access to internal kdbus policy ++ * access ++ */ ++static struct kdbus_policy_db_entry_access * ++kdbus_policy_make_access(const struct kdbus_policy_access *uaccess) ++{ ++ int ret; ++ struct kdbus_policy_db_entry_access *a; ++ ++ a = kzalloc(sizeof(*a), GFP_KERNEL); ++ if (!a) ++ return ERR_PTR(-ENOMEM); ++ ++ ret = -EINVAL; ++ switch (uaccess->access) { ++ case KDBUS_POLICY_SEE: ++ case KDBUS_POLICY_TALK: ++ case KDBUS_POLICY_OWN: ++ a->access = uaccess->access; ++ break; ++ default: ++ goto err; ++ } ++ ++ switch (uaccess->type) { ++ case KDBUS_POLICY_ACCESS_USER: ++ a->uid = make_kuid(current_user_ns(), uaccess->id); ++ if (!uid_valid(a->uid)) ++ goto err; ++ ++ break; ++ case KDBUS_POLICY_ACCESS_GROUP: ++ a->gid = make_kgid(current_user_ns(), uaccess->id); ++ if (!gid_valid(a->gid)) ++ goto err; ++ ++ break; ++ case KDBUS_POLICY_ACCESS_WORLD: ++ break; ++ default: ++ goto err; ++ } ++ ++ a->type = uaccess->type; ++ ++ return a; ++ ++err: ++ kfree(a); ++ return ERR_PTR(ret); ++} ++ ++/** ++ * kdbus_policy_set() - set a connection's policy rules ++ * @db: The policy database ++ * @items: A list of kdbus_item elements that contain both ++ * names and access rules to set. ++ * @items_size: The total size of the items. ++ * @max_policies: The maximum number of policy entries to allow. ++ * Pass 0 for no limit. ++ * @allow_wildcards: Boolean value whether wildcard entries (such ++ * ending on '.*') should be allowed. ++ * @owner: The owner of the new policy items. ++ * ++ * This function sets a new set of policies for a given owner. The names and ++ * access rules are gathered by walking the list of items passed in as ++ * argument. An item of type KDBUS_ITEM_NAME is expected before any number of ++ * KDBUS_ITEM_POLICY_ACCESS items. If there are more repetitions of this ++ * pattern than denoted in @max_policies, -EINVAL is returned. ++ * ++ * In order to allow atomic replacement of rules, the function first removes ++ * all entries that have been created for the given owner previously. ++ * ++ * Callers to this function must make sur that the owner is a custom ++ * endpoint, or if the endpoint is a default endpoint, then it must be ++ * either a policy holder or an activator. ++ * ++ * Return: 0 on success, negative errno on failure. ++ */ ++int kdbus_policy_set(struct kdbus_policy_db *db, ++ const struct kdbus_item *items, ++ size_t items_size, ++ size_t max_policies, ++ bool allow_wildcards, ++ const void *owner) ++{ ++ struct kdbus_policy_db_entry_access *a; ++ struct kdbus_policy_db_entry *e, *p; ++ const struct kdbus_item *item; ++ struct hlist_node *tmp; ++ HLIST_HEAD(entries); ++ HLIST_HEAD(restore); ++ size_t count = 0; ++ int i, ret = 0; ++ u32 hash; ++ ++ /* Walk the list of items and look for new policies */ ++ e = NULL; ++ KDBUS_ITEMS_FOREACH(item, items, items_size) { ++ switch (item->type) { ++ case KDBUS_ITEM_NAME: { ++ size_t len; ++ ++ if (max_policies && ++count > max_policies) { ++ ret = -E2BIG; ++ goto exit; ++ } ++ ++ if (!kdbus_name_is_valid(item->str, true)) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ e = kzalloc(sizeof(*e), GFP_KERNEL); ++ if (!e) { ++ ret = -ENOMEM; ++ goto exit; ++ } ++ ++ INIT_LIST_HEAD(&e->access_list); ++ e->owner = owner; ++ hlist_add_head(&e->hentry, &entries); ++ ++ e->name = kstrdup(item->str, GFP_KERNEL); ++ if (!e->name) { ++ ret = -ENOMEM; ++ goto exit; ++ } ++ ++ /* ++ * If a supplied name ends with an '.*', cut off that ++ * part, only store anything before it, and mark the ++ * entry as wildcard. ++ */ ++ len = strlen(e->name); ++ if (len > 2 && ++ e->name[len - 3] == '.' && ++ e->name[len - 2] == '*') { ++ if (!allow_wildcards) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ e->name[len - 3] = '\0'; ++ e->wildcard = true; ++ } ++ ++ break; ++ } ++ ++ case KDBUS_ITEM_POLICY_ACCESS: ++ if (!e) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ a = kdbus_policy_make_access(&item->policy_access); ++ if (IS_ERR(a)) { ++ ret = PTR_ERR(a); ++ goto exit; ++ } ++ ++ list_add_tail(&a->list, &e->access_list); ++ break; ++ } ++ } ++ ++ down_write(&db->entries_rwlock); ++ ++ /* remember previous entries to restore in case of failure */ ++ hash_for_each_safe(db->entries_hash, i, tmp, e, hentry) ++ if (e->owner == owner) { ++ hash_del(&e->hentry); ++ hlist_add_head(&e->hentry, &restore); ++ } ++ ++ hlist_for_each_entry_safe(e, tmp, &entries, hentry) { ++ /* prevent duplicates */ ++ hash = kdbus_strhash(e->name); ++ hash_for_each_possible(db->entries_hash, p, hentry, hash) ++ if (strcmp(e->name, p->name) == 0 && ++ e->wildcard == p->wildcard) { ++ ret = -EEXIST; ++ goto restore; ++ } ++ ++ hlist_del(&e->hentry); ++ hash_add(db->entries_hash, &e->hentry, hash); ++ } ++ ++restore: ++ /* if we failed, flush all entries we added so far */ ++ if (ret < 0) ++ __kdbus_policy_remove_owner(db, owner); ++ ++ /* if we failed, restore entries, otherwise release them */ ++ hlist_for_each_entry_safe(e, tmp, &restore, hentry) { ++ hlist_del(&e->hentry); ++ if (ret < 0) { ++ hash = kdbus_strhash(e->name); ++ hash_add(db->entries_hash, &e->hentry, hash); ++ } else { ++ kdbus_policy_entry_free(e); ++ } ++ } ++ ++ up_write(&db->entries_rwlock); ++ ++exit: ++ hlist_for_each_entry_safe(e, tmp, &entries, hentry) { ++ hlist_del(&e->hentry); ++ kdbus_policy_entry_free(e); ++ } ++ ++ return ret; ++} +diff --git a/ipc/kdbus/policy.h b/ipc/kdbus/policy.h +new file mode 100644 +index 000000000000..15dd7bc12068 +--- /dev/null ++++ b/ipc/kdbus/policy.h +@@ -0,0 +1,51 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * Copyright (C) 2013-2015 Linux Foundation ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#ifndef __KDBUS_POLICY_H ++#define __KDBUS_POLICY_H ++ ++#include <linux/hashtable.h> ++#include <linux/rwsem.h> ++ ++struct kdbus_conn; ++struct kdbus_item; ++ ++/** ++ * struct kdbus_policy_db - policy database ++ * @entries_hash: Hashtable of entries ++ * @entries_rwlock: Mutex to protect the database's access entries ++ */ ++struct kdbus_policy_db { ++ DECLARE_HASHTABLE(entries_hash, 6); ++ struct rw_semaphore entries_rwlock; ++}; ++ ++void kdbus_policy_db_init(struct kdbus_policy_db *db); ++void kdbus_policy_db_clear(struct kdbus_policy_db *db); ++ ++int kdbus_policy_query_unlocked(struct kdbus_policy_db *db, ++ const struct cred *cred, const char *name, ++ unsigned int hash); ++int kdbus_policy_query(struct kdbus_policy_db *db, const struct cred *cred, ++ const char *name, unsigned int hash); ++ ++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/kdbus-add-selftests.patch b/kdbus-add-selftests.patch new file mode 100644 index 000000000..911ac4e81 --- /dev/null +++ b/kdbus-add-selftests.patch @@ -0,0 +1,11448 @@ +From: Daniel Mack <daniel@zonque.org> +Date: Sat, 13 Sep 2014 23:15:02 +0200 +Subject: [PATCH] kdbus: add selftests + +This patch adds an extensive test suite for kdbus that checks the most +important code paths in the driver. The idea is to extend the test +suite over time. + +Also, this code can serve as another example for how to use the kernel +API from userspace. + +The code in the kdbus test suite makes use of the ioctl wrappers +provided by samples/kdbus/kdbus-api.h. + +Signed-off-by: Daniel Mack <daniel@zonque.org> +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Djalal Harouni <tixxdz@opendz.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + tools/testing/selftests/Makefile | 1 + + tools/testing/selftests/kdbus/.gitignore | 3 + + tools/testing/selftests/kdbus/Makefile | 46 + + tools/testing/selftests/kdbus/kdbus-enum.c | 94 ++ + tools/testing/selftests/kdbus/kdbus-enum.h | 14 + + tools/testing/selftests/kdbus/kdbus-test.c | 923 ++++++++++++ + tools/testing/selftests/kdbus/kdbus-test.h | 85 ++ + tools/testing/selftests/kdbus/kdbus-util.c | 1615 +++++++++++++++++++++ + tools/testing/selftests/kdbus/kdbus-util.h | 222 +++ + tools/testing/selftests/kdbus/test-activator.c | 318 ++++ + tools/testing/selftests/kdbus/test-attach-flags.c | 750 ++++++++++ + tools/testing/selftests/kdbus/test-benchmark.c | 451 ++++++ + tools/testing/selftests/kdbus/test-bus.c | 175 +++ + tools/testing/selftests/kdbus/test-chat.c | 122 ++ + tools/testing/selftests/kdbus/test-connection.c | 616 ++++++++ + tools/testing/selftests/kdbus/test-daemon.c | 65 + + tools/testing/selftests/kdbus/test-endpoint.c | 341 +++++ + tools/testing/selftests/kdbus/test-fd.c | 789 ++++++++++ + tools/testing/selftests/kdbus/test-free.c | 64 + + tools/testing/selftests/kdbus/test-match.c | 441 ++++++ + tools/testing/selftests/kdbus/test-message.c | 731 ++++++++++ + tools/testing/selftests/kdbus/test-metadata-ns.c | 506 +++++++ + tools/testing/selftests/kdbus/test-monitor.c | 176 +++ + tools/testing/selftests/kdbus/test-names.c | 194 +++ + tools/testing/selftests/kdbus/test-policy-ns.c | 632 ++++++++ + tools/testing/selftests/kdbus/test-policy-priv.c | 1269 ++++++++++++++++ + tools/testing/selftests/kdbus/test-policy.c | 80 + + tools/testing/selftests/kdbus/test-sync.c | 369 +++++ + tools/testing/selftests/kdbus/test-timeout.c | 99 ++ + 29 files changed, 11191 insertions(+) + create mode 100644 tools/testing/selftests/kdbus/.gitignore + create mode 100644 tools/testing/selftests/kdbus/Makefile + create mode 100644 tools/testing/selftests/kdbus/kdbus-enum.c + create mode 100644 tools/testing/selftests/kdbus/kdbus-enum.h + create mode 100644 tools/testing/selftests/kdbus/kdbus-test.c + create mode 100644 tools/testing/selftests/kdbus/kdbus-test.h + create mode 100644 tools/testing/selftests/kdbus/kdbus-util.c + create mode 100644 tools/testing/selftests/kdbus/kdbus-util.h + create mode 100644 tools/testing/selftests/kdbus/test-activator.c + create mode 100644 tools/testing/selftests/kdbus/test-attach-flags.c + create mode 100644 tools/testing/selftests/kdbus/test-benchmark.c + create mode 100644 tools/testing/selftests/kdbus/test-bus.c + create mode 100644 tools/testing/selftests/kdbus/test-chat.c + create mode 100644 tools/testing/selftests/kdbus/test-connection.c + create mode 100644 tools/testing/selftests/kdbus/test-daemon.c + create mode 100644 tools/testing/selftests/kdbus/test-endpoint.c + create mode 100644 tools/testing/selftests/kdbus/test-fd.c + create mode 100644 tools/testing/selftests/kdbus/test-free.c + create mode 100644 tools/testing/selftests/kdbus/test-match.c + create mode 100644 tools/testing/selftests/kdbus/test-message.c + create mode 100644 tools/testing/selftests/kdbus/test-metadata-ns.c + create mode 100644 tools/testing/selftests/kdbus/test-monitor.c + create mode 100644 tools/testing/selftests/kdbus/test-names.c + create mode 100644 tools/testing/selftests/kdbus/test-policy-ns.c + create mode 100644 tools/testing/selftests/kdbus/test-policy-priv.c + create mode 100644 tools/testing/selftests/kdbus/test-policy.c + create mode 100644 tools/testing/selftests/kdbus/test-sync.c + create mode 100644 tools/testing/selftests/kdbus/test-timeout.c + +diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile +index 24ae9e829e9a..3af31afa0d13 100644 +--- a/tools/testing/selftests/Makefile ++++ b/tools/testing/selftests/Makefile +@@ -6,6 +6,7 @@ TARGETS += firmware + TARGETS += ftrace + TARGETS += futex + TARGETS += kcmp ++TARGETS += kdbus + TARGETS += memfd + TARGETS += memory-hotplug + TARGETS += mount +diff --git a/tools/testing/selftests/kdbus/.gitignore b/tools/testing/selftests/kdbus/.gitignore +new file mode 100644 +index 000000000000..7b421f76c888 +--- /dev/null ++++ b/tools/testing/selftests/kdbus/.gitignore +@@ -0,0 +1,3 @@ ++*.7 ++manpage.* ++*.proc +diff --git a/tools/testing/selftests/kdbus/Makefile b/tools/testing/selftests/kdbus/Makefile +new file mode 100644 +index 000000000000..f6cfab26f315 +--- /dev/null ++++ b/tools/testing/selftests/kdbus/Makefile +@@ -0,0 +1,46 @@ ++CFLAGS += -I../../../../usr/include/ ++CFLAGS += -I../../../../samples/kdbus/ ++CFLAGS += -I../../../../include/uapi/ ++CFLAGS += -std=gnu99 ++CFLAGS += -DKBUILD_MODNAME=\"kdbus\" -D_GNU_SOURCE ++LDLIBS = -pthread -lcap -lm ++ ++OBJS= \ ++ kdbus-enum.o \ ++ kdbus-util.o \ ++ kdbus-test.o \ ++ kdbus-test.o \ ++ test-activator.o \ ++ test-attach-flags.o \ ++ test-benchmark.o \ ++ test-bus.o \ ++ test-chat.o \ ++ test-connection.o \ ++ test-daemon.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-sync.o \ ++ test-timeout.o ++ ++all: kdbus-test ++ ++%.o: %.c ++ gcc $(CFLAGS) -c $< -o $@ ++ ++kdbus-test: $(OBJS) ++ gcc $(CFLAGS) $^ $(LDLIBS) -o $@ ++ ++run_tests: ++ ./kdbus-test --tap ++ ++clean: ++ rm -f *.o kdbus-test +diff --git a/tools/testing/selftests/kdbus/kdbus-enum.c b/tools/testing/selftests/kdbus/kdbus-enum.c +new file mode 100644 +index 000000000000..4f1e5797895f +--- /dev/null ++++ b/tools/testing/selftests/kdbus/kdbus-enum.c +@@ -0,0 +1,94 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#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 "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_ENDPOINT_MAKE), ++ ENUM(KDBUS_CMD_HELLO), ++ ENUM(KDBUS_CMD_SEND), ++ ENUM(KDBUS_CMD_RECV), ++ ENUM(KDBUS_CMD_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_MAKE_NAME), ++ ENUM(KDBUS_ITEM_ATTACH_FLAGS_SEND), ++ ENUM(KDBUS_ITEM_ATTACH_FLAGS_RECV), ++ ENUM(KDBUS_ITEM_ID), ++ ENUM(KDBUS_ITEM_NAME), ++ ENUM(KDBUS_ITEM_TIMESTAMP), ++ ENUM(KDBUS_ITEM_CREDS), ++ ENUM(KDBUS_ITEM_PIDS), ++ ENUM(KDBUS_ITEM_AUXGROUPS), ++ ENUM(KDBUS_ITEM_OWNED_NAME), ++ ENUM(KDBUS_ITEM_TID_COMM), ++ ENUM(KDBUS_ITEM_PID_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_DESCRIPTION), ++ 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); +diff --git a/tools/testing/selftests/kdbus/kdbus-enum.h b/tools/testing/selftests/kdbus/kdbus-enum.h +new file mode 100644 +index 000000000000..a67cec3512a7 +--- /dev/null ++++ b/tools/testing/selftests/kdbus/kdbus-enum.h +@@ -0,0 +1,14 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++#pragma once ++ ++const char *enum_CMD(long long id); ++const char *enum_MSG(long long id); ++const char *enum_MATCH(long long id); ++const char *enum_PAYLOAD(long long id); +diff --git a/tools/testing/selftests/kdbus/kdbus-test.c b/tools/testing/selftests/kdbus/kdbus-test.c +new file mode 100644 +index 000000000000..a43674ccdeb0 +--- /dev/null ++++ b/tools/testing/selftests/kdbus/kdbus-test.c +@@ -0,0 +1,923 @@ ++#include <errno.h> ++#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 <signal.h> ++#include <sys/mount.h> ++#include <sys/prctl.h> ++#include <sys/wait.h> ++#include <sys/syscall.h> ++#include <sys/eventfd.h> ++#include <linux/sched.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; ++}; ++ ++struct kdbus_test_args { ++ bool mntns; ++ bool pidns; ++ bool userns; ++ char *uid_map; ++ char *gid_map; ++ int loop; ++ int wait; ++ int fork; ++ int tap_output; ++ char *module; ++ char *root; ++ char *test; ++ char *busname; ++ char *mask_param_path; ++}; ++ ++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 daemon", ++ .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 = "message-quota", ++ .desc = "message quotas are enforced", ++ .func = kdbus_test_message_quota, ++ .flags = TEST_CREATE_BUS, ++ }, ++ { ++ .name = "memory-access", ++ .desc = "memory access", ++ .func = kdbus_test_memory_access, ++ .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 different 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-replace", ++ .desc = "replace of matches with the same cookie", ++ .func = kdbus_test_match_replace, ++ .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 = "benchmark", ++ .desc = "benchmark", ++ .func = kdbus_test_benchmark, ++ .flags = TEST_CREATE_BUS, ++ }, ++ { ++ .name = "benchmark-nomemfds", ++ .desc = "benchmark without using memfds", ++ .func = kdbus_test_benchmark_nomemfds, ++ .flags = TEST_CREATE_BUS, ++ }, ++ { ++ .name = "benchmark-uds", ++ .desc = "benchmark comparison to UDS", ++ .func = kdbus_test_benchmark_uds, ++ .flags = TEST_CREATE_BUS, ++ }, ++ { ++ /* Last test */ ++ .name = "attach-flags", ++ .desc = "attach flags mask", ++ .func = kdbus_test_attach_flags, ++ .flags = 0, ++ }, ++}; ++ ++#define N_TESTS ((int) (sizeof(tests) / sizeof(tests[0]))) ++ ++static int test_prepare_env(const struct kdbus_test *t, ++ const struct kdbus_test_args *args, ++ struct kdbus_test_env *env) ++{ ++ if (t->flags & TEST_CREATE_BUS) { ++ char *s; ++ char *n = NULL; ++ int ret; ++ ++ asprintf(&s, "%s/control", args->root); ++ ++ env->control_fd = open(s, O_RDWR); ++ free(s); ++ ASSERT_RETURN(env->control_fd >= 0); ++ ++ if (!args->busname) { ++ n = unique_name("test-bus"); ++ ASSERT_RETURN(n); ++ } ++ ++ ret = kdbus_create_bus(env->control_fd, ++ args->busname ?: n, ++ _KDBUS_ATTACH_ALL, ++ _KDBUS_ATTACH_ALL, &s); ++ free(n); ++ ASSERT_RETURN(ret == 0); ++ ++ asprintf(&env->buspath, "%s/%s/bus", args->root, s); ++ free(s); ++ } ++ ++ if (t->flags & TEST_CREATE_CONN) { ++ env->conn = kdbus_hello(env->buspath, 0, NULL, 0); ++ ASSERT_RETURN(env->conn); ++ } ++ ++ env->root = args->root; ++ env->module = args->module; ++ env->mask_param_path = args->mask_param_path; ++ ++ 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 struct kdbus_test_args *kdbus_args, ++ int wait) ++{ ++ int ret; ++ struct kdbus_test_env env = {}; ++ ++ ret = test_prepare_env(t, kdbus_args, &env); ++ 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 int test_run_forked(const struct kdbus_test *t, ++ const struct kdbus_test_args *kdbus_args, ++ int wait) ++{ ++ int ret; ++ pid_t pid; ++ ++ pid = fork(); ++ if (pid < 0) { ++ return TEST_ERR; ++ } else if (pid == 0) { ++ ret = test_run(t, kdbus_args, wait); ++ _exit(ret); ++ } ++ ++ pid = waitpid(pid, &ret, 0); ++ if (pid <= 0) ++ return TEST_ERR; ++ else if (!WIFEXITED(ret)) ++ return TEST_ERR; ++ else ++ return WEXITSTATUS(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 start_all_tests(struct kdbus_test_args *kdbus_args) ++{ ++ int ret; ++ unsigned int fail_cnt = 0; ++ unsigned int skip_cnt = 0; ++ unsigned int ok_cnt = 0; ++ unsigned int i; ++ ++ if (kdbus_args->tap_output) { ++ printf("1..%d\n", N_TESTS); ++ fflush(stdout); ++ } ++ ++ kdbus_util_verbose = false; ++ ++ for (i = 0; i < N_TESTS; i++) { ++ const struct kdbus_test *t = tests + i; ++ ++ if (!kdbus_args->tap_output) { ++ unsigned int n; ++ ++ printf("Testing %s (%s) ", t->desc, t->name); ++ for (n = 0; n < 60 - strlen(t->desc) - strlen(t->name); n++) ++ printf("."); ++ printf(" "); ++ } ++ ++ ret = test_run_forked(t, kdbus_args, 0); ++ switch (ret) { ++ case TEST_OK: ++ ok_cnt++; ++ break; ++ case TEST_SKIP: ++ skip_cnt++; ++ break; ++ case TEST_ERR: ++ fail_cnt++; ++ break; ++ } ++ ++ if (kdbus_args->tap_output) { ++ printf("%sok %d - %s%s (%s)\n", ++ (ret == TEST_ERR) ? "not " : "", i + 1, ++ (ret == TEST_SKIP) ? "# SKIP " : "", ++ t->desc, t->name); ++ fflush(stdout); ++ } else { ++ print_test_result(ret); ++ printf("\n"); ++ } ++ } ++ ++ if (kdbus_args->tap_output) ++ printf("Failed %d/%d tests, %.2f%% okay\n", fail_cnt, N_TESTS, ++ 100.0 - (fail_cnt * 100.0) / ((float) N_TESTS)); ++ else ++ printf("\nSUMMARY: %u tests passed, %u skipped, %u failed\n", ++ ok_cnt, skip_cnt, fail_cnt); ++ ++ return fail_cnt > 0 ? TEST_ERR : TEST_OK; ++} ++ ++static int start_one_test(struct kdbus_test_args *kdbus_args) ++{ ++ int i, ret; ++ bool test_found = false; ++ ++ for (i = 0; i < N_TESTS; i++) { ++ const struct kdbus_test *t = tests + i; ++ ++ if (strcmp(t->name, kdbus_args->test)) ++ continue; ++ ++ do { ++ test_found = true; ++ if (kdbus_args->fork) ++ ret = test_run_forked(t, kdbus_args, ++ kdbus_args->wait); ++ else ++ ret = test_run(t, kdbus_args, ++ kdbus_args->wait); ++ ++ printf("Testing %s: ", t->desc); ++ print_test_result(ret); ++ printf("\n"); ++ ++ if (ret != TEST_OK) ++ break; ++ } while (kdbus_args->loop); ++ ++ return ret; ++ } ++ ++ if (!test_found) { ++ printf("Unknown test-id '%s'\n", kdbus_args->test); ++ return TEST_ERR; ++ } ++ ++ return TEST_OK; ++} ++ ++static void usage(const char *argv0) ++{ ++ unsigned int i, j; ++ ++ printf("Usage: %s [options]\n" ++ "Options:\n" ++ "\t-a, --tap Output test results in TAP format\n" ++ "\t-m, --module <module> Kdbus module name\n" ++ "\t-x, --loop Run in a loop\n" ++ "\t-f, --fork Fork before running a test\n" ++ "\t-h, --help Print this help\n" ++ "\t-r, --root <root> Toplevel of the kdbus hierarchy\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" ++ "\t --mntns New mount namespace\n" ++ "\t --pidns New PID namespace\n" ++ "\t --userns New user namespace\n" ++ "\t --uidmap uid_map UID map for user namespace\n" ++ "\t --gidmap gid_map GID map for user namespace\n" ++ "\n", argv0); ++ ++ printf("By default, all test are run once, and a summary is printed.\n" ++ "Available tests for --test:\n\n"); ++ ++ for (i = 0; i < N_TESTS; i++) { ++ const struct kdbus_test *t = tests + i; ++ ++ printf("\t%s", t->name); ++ ++ for (j = 0; j < 24 - strlen(t->name); j++) ++ 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); ++} ++ ++void print_kdbus_test_args(struct kdbus_test_args *args) ++{ ++ if (args->userns || args->pidns || args->mntns) ++ printf("# Starting tests in new %s%s%s namespaces%s\n", ++ args->mntns ? "MOUNT " : "", ++ args->pidns ? "PID " : "", ++ args->userns ? "USER " : "", ++ args->mntns ? ", kdbusfs will be remounted" : ""); ++ else ++ printf("# Starting tests in the same namespaces\n"); ++} ++ ++void print_metadata_support(void) ++{ ++ bool no_meta_audit, no_meta_cgroups, no_meta_seclabel; ++ ++ /* ++ * KDBUS_ATTACH_CGROUP, KDBUS_ATTACH_AUDIT and ++ * KDBUS_ATTACH_SECLABEL ++ */ ++ no_meta_audit = !config_auditsyscall_is_enabled(); ++ no_meta_cgroups = !config_cgroups_is_enabled(); ++ no_meta_seclabel = !config_security_is_enabled(); ++ ++ if (no_meta_audit | no_meta_cgroups | no_meta_seclabel) ++ printf("# Starting tests without %s%s%s metadata support\n", ++ no_meta_audit ? "AUDIT " : "", ++ no_meta_cgroups ? "CGROUP " : "", ++ no_meta_seclabel ? "SECLABEL " : ""); ++ else ++ printf("# Starting tests with full metadata support\n"); ++} ++ ++int run_tests(struct kdbus_test_args *kdbus_args) ++{ ++ int ret; ++ static char control[4096]; ++ ++ snprintf(control, sizeof(control), "%s/control", kdbus_args->root); ++ ++ if (access(control, W_OK) < 0) { ++ printf("Unable to locate control node at '%s'.\n", ++ control); ++ return TEST_ERR; ++ } ++ ++ if (kdbus_args->test) { ++ ret = start_one_test(kdbus_args); ++ } else { ++ do { ++ ret = start_all_tests(kdbus_args); ++ if (ret != TEST_OK) ++ break; ++ } while (kdbus_args->loop); ++ } ++ ++ return ret; ++} ++ ++static void nop_handler(int sig) {} ++ ++static int test_prepare_mounts(struct kdbus_test_args *kdbus_args) ++{ ++ int ret; ++ char kdbusfs[64] = {'\0'}; ++ ++ snprintf(kdbusfs, sizeof(kdbusfs), "%sfs", kdbus_args->module); ++ ++ /* make current mount slave */ ++ ret = mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL); ++ if (ret < 0) { ++ ret = -errno; ++ printf("error mount() root: %d (%m)\n", ret); ++ return ret; ++ } ++ ++ /* Remount procfs since we need it in our tests */ ++ if (kdbus_args->pidns) { ++ ret = mount("proc", "/proc", "proc", ++ MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL); ++ if (ret < 0) { ++ ret = -errno; ++ printf("error mount() /proc : %d (%m)\n", ret); ++ return ret; ++ } ++ } ++ ++ /* Remount kdbusfs */ ++ ret = mount(kdbusfs, kdbus_args->root, kdbusfs, ++ MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL); ++ if (ret < 0) { ++ ret = -errno; ++ printf("error mount() %s :%d (%m)\n", kdbusfs, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++int run_tests_in_namespaces(struct kdbus_test_args *kdbus_args) ++{ ++ int ret; ++ int efd = -1; ++ int status; ++ pid_t pid, rpid; ++ struct sigaction oldsa; ++ struct sigaction sa = { ++ .sa_handler = nop_handler, ++ .sa_flags = SA_NOCLDSTOP, ++ }; ++ ++ efd = eventfd(0, EFD_CLOEXEC); ++ if (efd < 0) { ++ ret = -errno; ++ printf("eventfd() failed: %d (%m)\n", ret); ++ return TEST_ERR; ++ } ++ ++ ret = sigaction(SIGCHLD, &sa, &oldsa); ++ if (ret < 0) { ++ ret = -errno; ++ printf("sigaction() failed: %d (%m)\n", ret); ++ return TEST_ERR; ++ } ++ ++ /* setup namespaces */ ++ pid = syscall(__NR_clone, SIGCHLD| ++ (kdbus_args->userns ? CLONE_NEWUSER : 0) | ++ (kdbus_args->mntns ? CLONE_NEWNS : 0) | ++ (kdbus_args->pidns ? CLONE_NEWPID : 0), NULL); ++ if (pid < 0) { ++ printf("clone() failed: %d (%m)\n", -errno); ++ return TEST_ERR; ++ } ++ ++ if (pid == 0) { ++ eventfd_t event_status = 0; ++ ++ ret = prctl(PR_SET_PDEATHSIG, SIGKILL); ++ if (ret < 0) { ++ ret = -errno; ++ printf("error prctl(): %d (%m)\n", ret); ++ _exit(TEST_ERR); ++ } ++ ++ /* reset sighandlers of childs */ ++ ret = sigaction(SIGCHLD, &oldsa, NULL); ++ if (ret < 0) { ++ ret = -errno; ++ printf("sigaction() failed: %d (%m)\n", ret); ++ _exit(TEST_ERR); ++ } ++ ++ ret = eventfd_read(efd, &event_status); ++ if (ret < 0 || event_status != 1) { ++ printf("error eventfd_read()\n"); ++ _exit(TEST_ERR); ++ } ++ ++ if (kdbus_args->mntns) { ++ ret = test_prepare_mounts(kdbus_args); ++ if (ret < 0) { ++ printf("error preparing mounts\n"); ++ _exit(TEST_ERR); ++ } ++ } ++ ++ ret = run_tests(kdbus_args); ++ _exit(ret); ++ } ++ ++ /* Setup userns mapping */ ++ if (kdbus_args->userns) { ++ ret = userns_map_uid_gid(pid, kdbus_args->uid_map, ++ kdbus_args->gid_map); ++ if (ret < 0) { ++ printf("error mapping uid and gid in userns\n"); ++ eventfd_write(efd, 2); ++ return TEST_ERR; ++ } ++ } ++ ++ ret = eventfd_write(efd, 1); ++ if (ret < 0) { ++ ret = -errno; ++ printf("error eventfd_write(): %d (%m)\n", ret); ++ return TEST_ERR; ++ } ++ ++ rpid = waitpid(pid, &status, 0); ++ ASSERT_RETURN_VAL(rpid == pid, TEST_ERR); ++ ++ close(efd); ++ ++ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) ++ return TEST_ERR; ++ ++ return TEST_OK; ++} ++ ++int start_tests(struct kdbus_test_args *kdbus_args) ++{ ++ int ret; ++ bool namespaces; ++ uint64_t kdbus_param_mask; ++ static char fspath[4096], parampath[4096]; ++ ++ namespaces = (kdbus_args->mntns || kdbus_args->pidns || ++ kdbus_args->userns); ++ ++ /* for pidns we need mntns set */ ++ if (kdbus_args->pidns && !kdbus_args->mntns) { ++ printf("Failed: please set both pid and mnt namesapces\n"); ++ return TEST_ERR; ++ } ++ ++ if (kdbus_args->userns) { ++ if (!config_user_ns_is_enabled()) { ++ printf("User namespace not supported\n"); ++ return TEST_ERR; ++ } ++ ++ if (!kdbus_args->uid_map || !kdbus_args->gid_map) { ++ printf("Failed: please specify uid or gid mapping\n"); ++ return TEST_ERR; ++ } ++ } ++ ++ print_kdbus_test_args(kdbus_args); ++ print_metadata_support(); ++ ++ /* setup kdbus paths */ ++ if (!kdbus_args->module) ++ kdbus_args->module = "kdbus"; ++ ++ if (!kdbus_args->root) { ++ snprintf(fspath, sizeof(fspath), "/sys/fs/%s", ++ kdbus_args->module); ++ kdbus_args->root = fspath; ++ } ++ ++ snprintf(parampath, sizeof(parampath), ++ "/sys/module/%s/parameters/attach_flags_mask", ++ kdbus_args->module); ++ kdbus_args->mask_param_path = parampath; ++ ++ ret = kdbus_sysfs_get_parameter_mask(kdbus_args->mask_param_path, ++ &kdbus_param_mask); ++ if (ret < 0) ++ return TEST_ERR; ++ ++ printf("# Starting tests with an attach_flags_mask=0x%llx\n", ++ (unsigned long long)kdbus_param_mask); ++ ++ /* Start tests */ ++ if (namespaces) ++ ret = run_tests_in_namespaces(kdbus_args); ++ else ++ ret = run_tests(kdbus_args); ++ ++ return ret; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int t, ret = 0; ++ struct kdbus_test_args *kdbus_args; ++ enum { ++ ARG_MNTNS = 0x100, ++ ARG_PIDNS, ++ ARG_USERNS, ++ ARG_UIDMAP, ++ ARG_GIDMAP, ++ }; ++ ++ kdbus_args = malloc(sizeof(*kdbus_args)); ++ if (!kdbus_args) { ++ printf("unable to malloc() kdbus_args\n"); ++ return EXIT_FAILURE; ++ } ++ ++ memset(kdbus_args, 0, sizeof(*kdbus_args)); ++ ++ static const struct option options[] = { ++ { "loop", no_argument, NULL, 'x' }, ++ { "help", no_argument, NULL, 'h' }, ++ { "root", required_argument, NULL, 'r' }, ++ { "test", required_argument, NULL, 't' }, ++ { "bus", required_argument, NULL, 'b' }, ++ { "wait", required_argument, NULL, 'w' }, ++ { "fork", no_argument, NULL, 'f' }, ++ { "module", required_argument, NULL, 'm' }, ++ { "tap", no_argument, NULL, 'a' }, ++ { "mntns", no_argument, NULL, ARG_MNTNS }, ++ { "pidns", no_argument, NULL, ARG_PIDNS }, ++ { "userns", no_argument, NULL, ARG_USERNS }, ++ { "uidmap", required_argument, NULL, ARG_UIDMAP }, ++ { "gidmap", required_argument, NULL, ARG_GIDMAP }, ++ {} ++ }; ++ ++ srand(time(NULL)); ++ ++ while ((t = getopt_long(argc, argv, "hxfm:r:t:b:w:a", options, NULL)) >= 0) { ++ switch (t) { ++ case 'x': ++ kdbus_args->loop = 1; ++ break; ++ ++ case 'm': ++ kdbus_args->module = optarg; ++ break; ++ ++ case 'r': ++ kdbus_args->root = optarg; ++ break; ++ ++ case 't': ++ kdbus_args->test = optarg; ++ break; ++ ++ case 'b': ++ kdbus_args->busname = optarg; ++ break; ++ ++ case 'w': ++ kdbus_args->wait = strtol(optarg, NULL, 10); ++ break; ++ ++ case 'f': ++ kdbus_args->fork = 1; ++ break; ++ ++ case 'a': ++ kdbus_args->tap_output = 1; ++ break; ++ ++ case ARG_MNTNS: ++ kdbus_args->mntns = true; ++ break; ++ ++ case ARG_PIDNS: ++ kdbus_args->pidns = true; ++ break; ++ ++ case ARG_USERNS: ++ kdbus_args->userns = true; ++ break; ++ ++ case ARG_UIDMAP: ++ kdbus_args->uid_map = optarg; ++ break; ++ ++ case ARG_GIDMAP: ++ kdbus_args->gid_map = optarg; ++ break; ++ ++ default: ++ case 'h': ++ usage(argv[0]); ++ } ++ } ++ ++ ret = start_tests(kdbus_args); ++ if (ret == TEST_ERR) ++ return EXIT_FAILURE; ++ ++ free(kdbus_args); ++ ++ return 0; ++} +diff --git a/tools/testing/selftests/kdbus/kdbus-test.h b/tools/testing/selftests/kdbus/kdbus-test.h +new file mode 100644 +index 000000000000..647331883763 +--- /dev/null ++++ b/tools/testing/selftests/kdbus/kdbus-test.h +@@ -0,0 +1,85 @@ ++#ifndef _TEST_KDBUS_H_ ++#define _TEST_KDBUS_H_ ++ ++struct kdbus_test_env { ++ char *buspath; ++ const char *root; ++ const char *module; ++ const char *mask_param_path; ++ 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_attach_flags(struct kdbus_test_env *env); ++int kdbus_test_benchmark(struct kdbus_test_env *env); ++int kdbus_test_benchmark_nomemfds(struct kdbus_test_env *env); ++int kdbus_test_benchmark_uds(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_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_replace(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_message_quota(struct kdbus_test_env *env); ++int kdbus_test_memory_access(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_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_ */ +diff --git a/tools/testing/selftests/kdbus/kdbus-util.c b/tools/testing/selftests/kdbus/kdbus-util.c +new file mode 100644 +index 000000000000..4b376ecfdbed +--- /dev/null ++++ b/tools/testing/selftests/kdbus/kdbus-util.c +@@ -0,0 +1,1615 @@ ++/* ++ * Copyright (C) 2013-2015 Daniel Mack ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2014-2015 Djalal Harouni ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#include <stdio.h> ++#include <stdarg.h> ++#include <string.h> ++#include <time.h> ++#include <inttypes.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/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-api.h" ++#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_sysfs_get_parameter_mask(const char *path, uint64_t *mask) ++{ ++ int ret; ++ FILE *file; ++ unsigned long long value; ++ ++ file = fopen(path, "r"); ++ if (!file) { ++ ret = -errno; ++ kdbus_printf("--- error fopen(): %d (%m)\n", ret); ++ return ret; ++ } ++ ++ ret = fscanf(file, "%llu", &value); ++ if (ret != 1) { ++ if (ferror(file)) ++ ret = -errno; ++ else ++ ret = -EIO; ++ ++ kdbus_printf("--- error fscanf(): %d\n", ret); ++ fclose(file); ++ return ret; ++ } ++ ++ *mask = (uint64_t)value; ++ ++ fclose(file); ++ ++ return 0; ++} ++ ++int kdbus_sysfs_set_parameter_mask(const char *path, uint64_t mask) ++{ ++ int ret; ++ FILE *file; ++ ++ file = fopen(path, "w"); ++ if (!file) { ++ ret = -errno; ++ kdbus_printf("--- error open(): %d (%m)\n", ret); ++ return ret; ++ } ++ ++ ret = fprintf(file, "%llu", (unsigned long long)mask); ++ if (ret <= 0) { ++ ret = -EIO; ++ kdbus_printf("--- error fprintf(): %d\n", ret); ++ } ++ ++ fclose(file); ++ ++ return ret > 0 ? 0 : ret; ++} ++ ++int kdbus_create_bus(int control_fd, const char *name, ++ uint64_t req_meta, uint64_t owner_meta, ++ char **path) ++{ ++ struct { ++ struct kdbus_cmd cmd; ++ ++ /* bloom size item */ ++ struct { ++ uint64_t size; ++ uint64_t type; ++ struct kdbus_bloom_parameter bloom; ++ } bp; ++ ++ /* required and owner metadata items */ ++ struct { ++ uint64_t size; ++ uint64_t type; ++ uint64_t flags; ++ } attach[2]; ++ ++ /* 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.attach[0].type = KDBUS_ITEM_ATTACH_FLAGS_RECV; ++ bus_make.attach[0].size = sizeof(bus_make.attach[0]); ++ bus_make.attach[0].flags = req_meta; ++ ++ bus_make.attach[1].type = KDBUS_ITEM_ATTACH_FLAGS_SEND; ++ bus_make.attach[1].size = sizeof(bus_make.attach[0]); ++ bus_make.attach[1].flags = owner_meta; ++ ++ bus_make.name.type = KDBUS_ITEM_MAKE_NAME; ++ bus_make.name.size = KDBUS_ITEM_HEADER_SIZE + ++ strlen(bus_make.name.str) + 1; ++ ++ bus_make.cmd.flags = KDBUS_MAKE_ACCESS_WORLD; ++ bus_make.cmd.size = sizeof(bus_make.cmd) + ++ bus_make.bp.size + ++ bus_make.attach[0].size + ++ bus_make.attach[1].size + ++ bus_make.name.size; ++ ++ kdbus_printf("Creating bus with name >%s< on control fd %d ...\n", ++ name, control_fd); ++ ++ ret = kdbus_cmd_bus_make(control_fd, &bus_make.cmd); ++ if (ret < 0) { ++ kdbus_printf("--- error when making bus: %d (%m)\n", ret); ++ return ret; ++ } ++ ++ if (ret == 0 && path) ++ *path = strdup(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) ++{ ++ struct kdbus_cmd_free cmd_free = {}; ++ 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_send = _KDBUS_ATTACH_ALL; ++ h.hello.attach_flags_recv = _KDBUS_ATTACH_ALL; ++ h.conn_name.type = KDBUS_ITEM_CONN_DESCRIPTION; ++ 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 = kdbus_cmd_hello(fd, (struct 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]); ++ ++ cmd_free.size = sizeof(cmd_free); ++ cmd_free.offset = h.hello.offset; ++ kdbus_cmd_free(fd, &cmd_free); ++ ++ conn = malloc(sizeof(*conn)); ++ if (!conn) { ++ kdbus_printf("unable to malloc()!?\n"); ++ return NULL; ++ } ++ ++ conn->buf = mmap(NULL, POOL_SIZE, PROT_READ, MAP_SHARED, 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); ++} ++ ++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_bus_creator_info(struct kdbus_conn *conn, ++ uint64_t flags, ++ uint64_t *offset) ++{ ++ struct kdbus_cmd_info *cmd; ++ size_t size = sizeof(*cmd); ++ int ret; ++ ++ cmd = alloca(size); ++ memset(cmd, 0, size); ++ cmd->size = size; ++ cmd->attach_flags = flags; ++ ++ ret = kdbus_cmd_bus_creator_info(conn->fd, cmd); ++ if (ret < 0) { ++ kdbus_printf("--- error when requesting info: %d (%m)\n", ret); ++ return ret; ++ } ++ ++ if (offset) ++ *offset = cmd->offset; ++ else ++ kdbus_free(conn, cmd->offset); ++ ++ return 0; ++} ++ ++int kdbus_conn_info(struct kdbus_conn *conn, uint64_t id, ++ const char *name, uint64_t flags, ++ uint64_t *offset) ++{ ++ struct kdbus_cmd_info *cmd; ++ size_t size = sizeof(*cmd); ++ struct kdbus_info *info; ++ int ret; ++ ++ if (name) ++ size += KDBUS_ITEM_HEADER_SIZE + strlen(name) + 1; ++ ++ cmd = alloca(size); ++ memset(cmd, 0, size); ++ cmd->size = size; ++ cmd->attach_flags = flags; ++ ++ 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 = kdbus_cmd_conn_info(conn->fd, cmd); ++ if (ret < 0) { ++ kdbus_printf("--- error when requesting info: %d (%m)\n", ret); ++ return ret; ++ } ++ ++ info = (struct kdbus_info *) (conn->buf + cmd->offset); ++ if (info->size != cmd->info_size) { ++ kdbus_printf("%s(): size mismatch: %d != %d\n", __func__, ++ (int) info->size, (int) cmd->info_size); ++ return -EIO; ++ } ++ ++ 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 | F_SEAL_SEAL); ++} ++ ++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; ++} ++ ++static 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, ++ uint64_t cmd_flags, ++ int cancel_fd) ++{ ++ struct kdbus_cmd_send *cmd; ++ 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(*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; ++ } ++ ++ if (dst_id == KDBUS_DST_ID_BROADCAST) ++ flags |= KDBUS_MSG_SIGNAL; ++ ++ 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); ++ ++ size = sizeof(*cmd); ++ if (cancel_fd != -1) ++ size += KDBUS_ITEM_SIZE(sizeof(cancel_fd)); ++ ++ cmd = malloc(size); ++ cmd->size = size; ++ cmd->flags = cmd_flags; ++ cmd->msg_address = (uintptr_t)msg; ++ ++ item = cmd->items; ++ ++ if (cancel_fd != -1) { ++ item->type = KDBUS_ITEM_CANCEL_FD; ++ item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(cancel_fd); ++ item->fds[0] = cancel_fd; ++ item = KDBUS_ITEM_NEXT(item); ++ } ++ ++ ret = kdbus_cmd_send(conn->fd, cmd); ++ if (memfd >= 0) ++ close(memfd); ++ ++ if (ret < 0) { ++ kdbus_printf("error sending message: %d (%m)\n", ret); ++ return ret; ++ } ++ ++ if (cmd_flags & KDBUS_SEND_SYNC_REPLY) { ++ struct kdbus_msg *reply; ++ ++ kdbus_printf("SYNC REPLY @offset %llu:\n", cmd->reply.offset); ++ reply = (struct kdbus_msg *)(conn->buf + cmd->reply.offset); ++ kdbus_msg_dump(conn, reply); ++ ++ kdbus_msg_free(reply); ++ ++ ret = kdbus_free(conn, cmd->reply.offset); ++ if (ret < 0) ++ return ret; ++ } ++ ++ free(msg); ++ free(cmd); ++ ++ 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) ++{ ++ return __kdbus_msg_send(conn, name, cookie, flags, timeout, priority, ++ dst_id, 0, -1); ++} ++ ++int kdbus_msg_send_sync(const struct kdbus_conn *conn, const char *name, ++ uint64_t cookie, uint64_t flags, uint64_t timeout, ++ int64_t priority, uint64_t dst_id, int cancel_fd) ++{ ++ return __kdbus_msg_send(conn, name, cookie, flags, timeout, priority, ++ dst_id, KDBUS_SEND_SYNC_REPLY, cancel_fd); ++} ++ ++int kdbus_msg_send_reply(const struct kdbus_conn *conn, ++ uint64_t reply_cookie, ++ uint64_t dst_id) ++{ ++ struct kdbus_cmd_send cmd = {}; ++ 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) { ++ kdbus_printf("unable to malloc()!?\n"); ++ return -ENOMEM; ++ } ++ ++ 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); ++ ++ cmd.size = sizeof(cmd); ++ cmd.msg_address = (uintptr_t)msg; ++ ++ ret = kdbus_cmd_send(conn->fd, &cmd); ++ if (ret < 0) ++ kdbus_printf("error sending message: %d (%m)\n", ret); ++ ++ free(msg); ++ ++ return ret; ++} ++ ++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_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_FDS: { ++ int i, n = (item->size - KDBUS_ITEM_HEADER_SIZE) / ++ sizeof(int); ++ ++ kdbus_printf(" +%s (%llu bytes, %d fds)\n", ++ enum_MSG(item->type), item->size, n); ++ ++ for (i = 0; i < n; i++) ++ kdbus_printf(" fd[%d] = %d\n", ++ i, item->fds[i]); ++ ++ 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, euid=%lld, suid=%lld, fsuid=%lld, " ++ "gid=%lld, egid=%lld, sgid=%lld, fsgid=%lld\n", ++ enum_MSG(item->type), item->size, ++ item->creds.uid, item->creds.euid, ++ item->creds.suid, item->creds.fsuid, ++ item->creds.gid, item->creds.egid, ++ item->creds.sgid, item->creds.fsgid); ++ break; ++ ++ case KDBUS_ITEM_PIDS: ++ kdbus_printf(" +%s (%llu bytes) pid=%lld, tid=%lld, ppid=%lld\n", ++ enum_MSG(item->type), item->size, ++ item->pids.pid, item->pids.tid, ++ item->pids.ppid); ++ 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_NAME: ++ 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_DESCRIPTION: ++ kdbus_printf(" +%s (%llu bytes) '%s' (%zu)\n", ++ enum_MSG(item->type), item->size, ++ item->str, strlen(item->str)); ++ break; ++ ++ case KDBUS_ITEM_OWNED_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=%u sessionid=%u\n", ++ enum_MSG(item->type), item->size, ++ item->audit.loginuid, 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 = { .size = sizeof(recv) }; ++ struct kdbus_msg *msg; ++ int ret; ++ ++ ret = kdbus_cmd_recv(conn->fd, &recv); ++ if (ret < 0) ++ return ret; ++ ++ msg = (struct kdbus_msg *)(conn->buf + recv.msg.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.msg.offset; ++ } else { ++ kdbus_msg_free(msg); ++ ++ ret = kdbus_free(conn, recv.msg.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.size = sizeof(cmd_free); ++ cmd_free.offset = offset; ++ cmd_free.flags = 0; ++ ++ ret = kdbus_cmd_free(conn->fd, &cmd_free); ++ if (ret < 0) { ++ kdbus_printf("KDBUS_CMD_FREE failed: %d (%m)\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++int kdbus_name_acquire(struct kdbus_conn *conn, ++ const char *name, uint64_t *flags) ++{ ++ struct kdbus_cmd *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 = kdbus_cmd_name_acquire(conn->fd, cmd_name); ++ if (ret < 0) { ++ kdbus_printf("error aquiring name: %s\n", strerror(-ret)); ++ return ret; ++ } ++ ++ kdbus_printf("%s(): flags after call: 0x%llx\n", __func__, ++ cmd_name->return_flags); ++ ++ if (flags) ++ *flags = cmd_name->return_flags; ++ ++ return 0; ++} ++ ++int kdbus_name_release(struct kdbus_conn *conn, const char *name) ++{ ++ struct kdbus_cmd *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 = kdbus_cmd_name_release(conn->fd, cmd_name); ++ if (ret < 0) { ++ kdbus_printf("error releasing name: %s\n", strerror(-ret)); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++int kdbus_list(struct kdbus_conn *conn, uint64_t flags) ++{ ++ struct kdbus_cmd_list cmd_list = {}; ++ struct kdbus_info *list, *name; ++ int ret; ++ ++ cmd_list.size = sizeof(cmd_list); ++ cmd_list.flags = flags; ++ ++ ret = kdbus_cmd_list(conn->fd, &cmd_list); ++ if (ret < 0) { ++ kdbus_printf("error listing names: %d (%m)\n", ret); ++ return ret; ++ } ++ ++ kdbus_printf("REGISTRY:\n"); ++ list = (struct kdbus_info *)(conn->buf + cmd_list.offset); ++ ++ KDBUS_FOREACH(name, list, cmd_list.list_size) { ++ uint64_t flags = 0; ++ struct kdbus_item *item; ++ const char *n = "MISSING-NAME"; ++ ++ if (name->size == sizeof(struct kdbus_cmd)) ++ continue; ++ ++ KDBUS_ITEM_FOREACH(item, name, items) ++ if (item->type == KDBUS_ITEM_OWNED_NAME) { ++ n = item->name.name; ++ flags = item->name.flags; ++ } ++ ++ kdbus_printf("%8llu flags=0x%08llx conn=0x%08llx '%s'\n", ++ name->id, (unsigned long long) flags, ++ name->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 attach_flags_send, ++ uint64_t attach_flags_recv) ++{ ++ int ret; ++ size_t size; ++ struct kdbus_cmd *update; ++ struct kdbus_item *item; ++ ++ size = sizeof(struct kdbus_cmd); ++ size += KDBUS_ITEM_SIZE(sizeof(uint64_t)) * 2; ++ ++ update = malloc(size); ++ if (!update) { ++ kdbus_printf("error malloc: %m\n"); ++ return -ENOMEM; ++ } ++ ++ memset(update, 0, size); ++ update->size = size; ++ ++ item = update->items; ++ ++ item->type = KDBUS_ITEM_ATTACH_FLAGS_SEND; ++ item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(uint64_t); ++ item->data64[0] = attach_flags_send; ++ item = KDBUS_ITEM_NEXT(item); ++ ++ item->type = KDBUS_ITEM_ATTACH_FLAGS_RECV; ++ item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(uint64_t); ++ item->data64[0] = attach_flags_recv; ++ item = KDBUS_ITEM_NEXT(item); ++ ++ ret = kdbus_cmd_update(conn->fd, update); ++ if (ret < 0) ++ 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; ++ struct kdbus_item *item; ++ size_t i, size; ++ int ret; ++ ++ size = sizeof(struct kdbus_cmd); ++ size += KDBUS_ITEM_SIZE(strlen(name) + 1); ++ size += num_access * KDBUS_ITEM_SIZE(sizeof(struct kdbus_policy_access)); ++ ++ update = malloc(size); ++ if (!update) { ++ kdbus_printf("error malloc: %m\n"); ++ return -ENOMEM; ++ } ++ ++ 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 = kdbus_cmd_update(conn->fd, update); ++ if (ret < 0) ++ kdbus_printf("error conn update: %d (%m)\n", ret); ++ ++ free(update); ++ ++ return ret; ++} ++ ++int kdbus_add_match_id(struct kdbus_conn *conn, uint64_t cookie, ++ uint64_t type, uint64_t id) ++{ ++ struct { ++ struct kdbus_cmd_match cmd; ++ struct { ++ uint64_t size; ++ uint64_t type; ++ struct kdbus_notify_id_change chg; ++ } item; ++ } buf; ++ int ret; ++ ++ memset(&buf, 0, sizeof(buf)); ++ ++ buf.cmd.size = sizeof(buf); ++ buf.cmd.cookie = cookie; ++ buf.item.size = sizeof(buf.item); ++ buf.item.type = type; ++ buf.item.chg.id = id; ++ ++ ret = kdbus_cmd_match_add(conn->fd, &buf.cmd); ++ if (ret < 0) ++ kdbus_printf("--- error adding conn match: %d (%m)\n", ret); ++ ++ 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 = kdbus_cmd_match_add(conn->fd, &buf.cmd); ++ if (ret < 0) ++ kdbus_printf("--- error adding conn match: %d (%m)\n", ret); ++ ++ return ret; ++} ++ ++static int all_ids_are_mapped(const char *path) ++{ ++ int ret; ++ FILE *file; ++ uint32_t inside_id, length; ++ ++ file = fopen(path, "r"); ++ if (!file) { ++ ret = -errno; ++ kdbus_printf("error fopen() %s: %d (%m)\n", ++ path, ret); ++ return ret; ++ } ++ ++ ret = fscanf(file, "%u\t%*u\t%u", &inside_id, &length); ++ if (ret != 2) { ++ if (ferror(file)) ++ ret = -errno; ++ else ++ ret = -EIO; ++ ++ kdbus_printf("--- error fscanf(): %d\n", ret); ++ fclose(file); ++ return ret; ++ } ++ ++ fclose(file); ++ ++ /* ++ * If length is 4294967295 which means the invalid uid ++ * (uid_t) -1 then we are able to map all uid/gids ++ */ ++ if (inside_id == 0 && length == (uid_t) -1) ++ return 1; ++ ++ return 0; ++} ++ ++int all_uids_gids_are_mapped() ++{ ++ int ret; ++ ++ ret = all_ids_are_mapped("/proc/self/uid_map"); ++ if (ret <= 0) { ++ kdbus_printf("--- error not all uids are mapped\n"); ++ return 0; ++ } ++ ++ ret = all_ids_are_mapped("/proc/self/gid_map"); ++ if (ret <= 0) { ++ kdbus_printf("--- error not all gids are mapped\n"); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++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; ++} ++ ++uint64_t now(clockid_t clock) ++{ ++ struct timespec spec; ++ ++ clock_gettime(clock, &spec); ++ return spec.tv_sec * 1000ULL * 1000ULL * 1000ULL + spec.tv_nsec; ++} ++ ++char *unique_name(const char *prefix) ++{ ++ unsigned int i; ++ uint64_t u_now; ++ char n[17]; ++ char *str; ++ int r; ++ ++ /* ++ * This returns a random string which is guaranteed to be ++ * globally unique across all calls to unique_name(). We ++ * compose the string as: ++ * <prefix>-<random>-<time> ++ * With: ++ * <prefix>: string provided by the caller ++ * <random>: a random alpha string of 16 characters ++ * <time>: the current time in micro-seconds since last boot ++ * ++ * The <random> part makes the string always look vastly different, ++ * the <time> part makes sure no two calls return the same string. ++ */ ++ ++ u_now = now(CLOCK_MONOTONIC); ++ ++ for (i = 0; i < sizeof(n) - 1; ++i) ++ n[i] = 'a' + (rand() % ('z' - 'a')); ++ n[sizeof(n) - 1] = 0; ++ ++ r = asprintf(&str, "%s-%s-%" PRIu64, prefix, n, u_now); ++ if (r < 0) ++ return NULL; ++ ++ return str; ++} ++ ++static int do_userns_map_id(pid_t pid, ++ const char *map_file, ++ const char *map_id) ++{ ++ int ret; ++ int fd; ++ char *map; ++ unsigned int i; ++ ++ map = strndupa(map_id, strlen(map_id)); ++ if (!map) { ++ ret = -errno; ++ kdbus_printf("error strndupa %s: %d (%m)\n", ++ map_file, ret); ++ return ret; ++ } ++ ++ for (i = 0; i < strlen(map); i++) ++ if (map[i] == ',') ++ map[i] = '\n'; ++ ++ 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, strlen(map)); ++ 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 fd, 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/setgroups", ++ (long) pid); ++ ++ fd = open(file_id, O_WRONLY); ++ if (fd >= 0) { ++ write(fd, "deny\n", 5); ++ close(fd); ++ } ++ ++ 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; ++} ++ ++int config_user_ns_is_enabled(void) ++{ ++ return (access("/proc/self/uid_map", F_OK) == 0); ++} ++ ++int config_auditsyscall_is_enabled(void) ++{ ++ return (access("/proc/self/loginuid", F_OK) == 0); ++} ++ ++int config_cgroups_is_enabled(void) ++{ ++ return (access("/proc/self/cgroup", F_OK) == 0); ++} ++ ++int config_security_is_enabled(void) ++{ ++ int fd; ++ int ret; ++ char buf[128]; ++ ++ /* CONFIG_SECURITY is disabled */ ++ if (access("/proc/self/attr/current", F_OK) != 0) ++ return 0; ++ ++ /* ++ * Now only if read() fails with -EINVAL then we assume ++ * that SECLABEL and LSM are disabled ++ */ ++ fd = open("/proc/self/attr/current", O_RDONLY|O_CLOEXEC); ++ if (fd < 0) ++ return 1; ++ ++ ret = read(fd, buf, sizeof(buf)); ++ if (ret == -1 && errno == EINVAL) ++ ret = 0; ++ else ++ ret = 1; ++ ++ close(fd); ++ ++ return ret; ++} +diff --git a/tools/testing/selftests/kdbus/kdbus-util.h b/tools/testing/selftests/kdbus/kdbus-util.h +new file mode 100644 +index 000000000000..50ff07140bdd +--- /dev/null ++++ b/tools/testing/selftests/kdbus/kdbus-util.h +@@ -0,0 +1,222 @@ ++/* ++ * Copyright (C) 2013-2015 Kay Sievers ++ * Copyright (C) 2013-2015 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 <time.h> ++#include <stdbool.h> ++#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) && \ ++ ((uint8_t *)(item) >= (uint8_t *)(head)); \ ++ item = KDBUS_ITEM_NEXT(item)) ++#define KDBUS_FOREACH(iter, first, _size) \ ++ for (iter = (first); \ ++ ((uint8_t *)(iter) < (uint8_t *)(first) + (_size)) && \ ++ ((uint8_t *)(iter) >= (uint8_t *)(first)); \ ++ iter = (void*)(((uint8_t *)iter) + KDBUS_ALIGN8((iter)->size))) ++ ++ ++#define _KDBUS_ATTACH_BITS_SET_NR (__builtin_popcountll(_KDBUS_ATTACH_ALL)) ++ ++/* Sum of KDBUS_ITEM_* that reflects _KDBUS_ATTACH_ALL */ ++#define KDBUS_ATTACH_ITEMS_TYPE_SUM \ ++ ((((_KDBUS_ATTACH_BITS_SET_NR - 1) * \ ++ ((_KDBUS_ATTACH_BITS_SET_NR - 1) + 1)) / 2 ) + \ ++ (_KDBUS_ITEM_ATTACH_BASE * _KDBUS_ATTACH_BITS_SET_NR)) ++ ++ ++#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); \ ++ ASSERT_EXIT_VAL(ret == 0, 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; })) ++ ++#define RUN_CLONE_CHILD(clone_ret, flags, _setup_, _child_body_, \ ++ _parent_setup_, _parent_body_) ({ \ ++ pid_t pid, rpid; \ ++ int ret; \ ++ int efd = -1; \ ++ \ ++ _setup_; \ ++ efd = eventfd(0, EFD_CLOEXEC); \ ++ ASSERT_RETURN(efd >= 0); \ ++ *clone_ret = 0; \ ++ pid = syscall(__NR_clone, flags, NULL); \ ++ if (pid == 0) { \ ++ eventfd_t event_status = 0; \ ++ ret = prctl(PR_SET_PDEATHSIG, SIGKILL); \ ++ ASSERT_EXIT(ret == 0); \ ++ ret = eventfd_read(efd, &event_status); \ ++ if (ret < 0 || event_status != 1) { \ ++ kdbus_printf("error eventfd_read()\n"); \ ++ _exit(EXIT_FAILURE); \ ++ } \ ++ _child_body_; \ ++ _exit(0); \ ++ } else if (pid > 0) { \ ++ _parent_setup_; \ ++ ret = eventfd_write(efd, 1); \ ++ ASSERT_RETURN(ret >= 0); \ ++ _parent_body_; \ ++ rpid = waitpid(pid, &ret, 0); \ ++ ASSERT_RETURN(rpid == pid); \ ++ ASSERT_RETURN(WIFEXITED(ret)); \ ++ ASSERT_RETURN(WEXITSTATUS(ret) == 0); \ ++ ret = TEST_OK; \ ++ } else { \ ++ ret = -errno; \ ++ *clone_ret = -errno; \ ++ } \ ++ close(efd); \ ++ ret; \ ++}) ++ ++/* 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; ++ unsigned char *buf; ++}; ++ ++int kdbus_sysfs_get_parameter_mask(const char *path, uint64_t *mask); ++int kdbus_sysfs_set_parameter_mask(const char *path, uint64_t mask); ++ ++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_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, ++ uint64_t req_meta, uint64_t owner_meta, ++ 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); ++int kdbus_msg_send_sync(const struct kdbus_conn *conn, const char *name, ++ uint64_t cookie, uint64_t flags, uint64_t timeout, ++ int64_t priority, uint64_t dst_id, int cancel_fd); ++int kdbus_msg_send_reply(const struct kdbus_conn *conn, ++ uint64_t reply_cookie, ++ 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); ++bool kdbus_item_in_message(struct kdbus_msg *msg, uint64_t type); ++int kdbus_bus_creator_info(struct kdbus_conn *conn, ++ uint64_t flags, ++ uint64_t *offset); ++int kdbus_conn_info(struct kdbus_conn *conn, uint64_t id, ++ const char *name, uint64_t flags, uint64_t *offset); ++void kdbus_conn_free(struct kdbus_conn *conn); ++int kdbus_conn_update_attach_flags(struct kdbus_conn *conn, ++ uint64_t attach_flags_send, ++ uint64_t attach_flags_recv); ++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_id(struct kdbus_conn *conn, uint64_t cookie, ++ uint64_t type, uint64_t id); ++int kdbus_add_match_empty(struct kdbus_conn *conn); ++ ++int all_uids_gids_are_mapped(); ++int drop_privileges(uid_t uid, gid_t gid); ++uint64_t now(clockid_t clock); ++char *unique_name(const char *prefix); ++ ++int userns_map_uid_gid(pid_t pid, ++ const char *map_uid, ++ const char *map_gid); ++int test_is_capable(int cap, ...); ++int config_user_ns_is_enabled(void); ++int config_auditsyscall_is_enabled(void); ++int config_cgroups_is_enabled(void); ++int config_security_is_enabled(void); +diff --git a/tools/testing/selftests/kdbus/test-activator.c b/tools/testing/selftests/kdbus/test-activator.c +new file mode 100644 +index 000000000000..3d1b76370ce8 +--- /dev/null ++++ b/tools/testing/selftests/kdbus/test-activator.c +@@ -0,0 +1,318 @@ ++#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/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); ++ ++ /* 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); ++ ++ 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 = getuid(); ++ 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_list(env->conn, KDBUS_LIST_NAMES | ++ KDBUS_LIST_UNIQUE | ++ KDBUS_LIST_ACTIVATORS | ++ KDBUS_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_list(env->conn, KDBUS_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 if all uids/gids are mapped */ ++ if (!all_uids_gids_are_mapped()) ++ return TEST_SKIP; ++ ++ /* 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; ++} +diff --git a/tools/testing/selftests/kdbus/test-attach-flags.c b/tools/testing/selftests/kdbus/test-attach-flags.c +new file mode 100644 +index 000000000000..deee7c332f25 +--- /dev/null ++++ b/tools/testing/selftests/kdbus/test-attach-flags.c +@@ -0,0 +1,750 @@ ++#include <stdio.h> ++#include <string.h> ++#include <stdlib.h> ++#include <stdbool.h> ++#include <stddef.h> ++#include <fcntl.h> ++#include <unistd.h> ++#include <stdint.h> ++#include <errno.h> ++#include <assert.h> ++#include <sys/capability.h> ++#include <sys/mman.h> ++#include <sys/stat.h> ++#include <sys/types.h> ++#include <linux/unistd.h> ++ ++#include "kdbus-api.h" ++#include "kdbus-test.h" ++#include "kdbus-util.h" ++#include "kdbus-enum.h" ++ ++/* ++ * Should be the sum of the currently supported and compiled-in ++ * KDBUS_ITEMS_* that reflect KDBUS_ATTACH_* flags. ++ */ ++static unsigned int KDBUS_TEST_ITEMS_SUM = KDBUS_ATTACH_ITEMS_TYPE_SUM; ++ ++static struct kdbus_conn *__kdbus_hello(const char *path, uint64_t flags, ++ uint64_t attach_flags_send, ++ uint64_t attach_flags_recv) ++{ ++ struct kdbus_cmd_free cmd_free = {}; ++ int ret, fd; ++ struct kdbus_conn *conn; ++ struct { ++ struct kdbus_cmd_hello hello; ++ ++ struct { ++ uint64_t size; ++ uint64_t type; ++ char str[16]; ++ } conn_name; ++ ++ uint8_t extra_items[0]; ++ } h; ++ ++ memset(&h, 0, sizeof(h)); ++ ++ 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_send = attach_flags_send; ++ h.hello.attach_flags_recv = attach_flags_recv; ++ h.conn_name.type = KDBUS_ITEM_CONN_DESCRIPTION; ++ 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 = kdbus_cmd_hello(fd, (struct kdbus_cmd_hello *) &h.hello); ++ if (ret < 0) { ++ kdbus_printf("--- error when saying hello: %d (%m)\n", ret); ++ return NULL; ++ } ++ ++ kdbus_printf("-- New connection ID : %llu\n", ++ (unsigned long long)h.hello.id); ++ ++ cmd_free.size = sizeof(cmd_free); ++ cmd_free.offset = h.hello.offset; ++ ret = kdbus_cmd_free(fd, &cmd_free); ++ if (ret < 0) ++ return NULL; ++ ++ conn = malloc(sizeof(*conn)); ++ if (!conn) { ++ kdbus_printf("unable to malloc()!?\n"); ++ return NULL; ++ } ++ ++ conn->buf = mmap(NULL, POOL_SIZE, PROT_READ, MAP_SHARED, fd, 0); ++ if (conn->buf == MAP_FAILED) { ++ ret = -errno; ++ free(conn); ++ close(fd); ++ kdbus_printf("--- error mmap: %d (%m)\n", ret); ++ return NULL; ++ } ++ ++ conn->fd = fd; ++ conn->id = h.hello.id; ++ return conn; ++} ++ ++static int kdbus_test_peers_creation(struct kdbus_test_env *env) ++{ ++ int ret; ++ int control_fd; ++ char *path; ++ char *busname; ++ char buspath[2048]; ++ char control_path[2048]; ++ uint64_t attach_flags_mask; ++ struct kdbus_conn *conn; ++ ++ snprintf(control_path, sizeof(control_path), ++ "%s/control", env->root); ++ ++ /* ++ * Set kdbus system-wide mask to 0, this has nothing ++ * to do with the following tests, bus and connection ++ * creation nor connection update, but we do it so we are ++ * sure that everything work as expected ++ */ ++ ++ attach_flags_mask = 0; ++ ret = kdbus_sysfs_set_parameter_mask(env->mask_param_path, ++ attach_flags_mask); ++ ASSERT_RETURN(ret == 0); ++ ++ ++ /* ++ * Create bus with a full set of ATTACH flags ++ */ ++ ++ control_fd = open(control_path, O_RDWR); ++ ASSERT_RETURN(control_fd >= 0); ++ ++ busname = unique_name("test-peers-creation-bus"); ++ ASSERT_RETURN(busname); ++ ++ ret = kdbus_create_bus(control_fd, busname, _KDBUS_ATTACH_ALL, ++ 0, &path); ++ ASSERT_RETURN(ret == 0); ++ ++ snprintf(buspath, sizeof(buspath), "%s/%s/bus", env->root, path); ++ ++ /* ++ * Create a connection with an empty send attach flags, or ++ * with just KDBUS_ATTACH_CREDS, this should fail ++ */ ++ conn = __kdbus_hello(buspath, 0, 0, 0); ++ ASSERT_RETURN(conn == NULL); ++ ASSERT_RETURN(errno == ECONNREFUSED); ++ ++ conn = __kdbus_hello(buspath, 0, KDBUS_ATTACH_CREDS, ++ _KDBUS_ATTACH_ALL); ++ ASSERT_RETURN(conn == NULL); ++ ASSERT_RETURN(errno == ECONNREFUSED); ++ ++ conn = __kdbus_hello(buspath, 0, _KDBUS_ATTACH_ALL, 0); ++ ASSERT_RETURN(conn); ++ ++ /* Try to cut back some send attach flags */ ++ ret = kdbus_conn_update_attach_flags(conn, ++ KDBUS_ATTACH_CREDS| ++ KDBUS_ATTACH_PIDS, ++ _KDBUS_ATTACH_ALL); ++ ASSERT_RETURN(ret == -EINVAL); ++ ++ ret = kdbus_conn_update_attach_flags(conn, ++ _KDBUS_ATTACH_ALL, 0); ++ ASSERT_RETURN(ret == 0); ++ ++ kdbus_conn_free(conn); ++ free(path); ++ free(busname); ++ close(control_fd); ++ ++ ++ /* Test a new bus with KDBUS_ATTACH_PIDS */ ++ ++ control_fd = open(control_path, O_RDWR); ++ ASSERT_RETURN(control_fd >= 0); ++ ++ busname = unique_name("test-peer-flags-bus"); ++ ASSERT_RETURN(busname); ++ ++ ret = kdbus_create_bus(control_fd, busname, KDBUS_ATTACH_PIDS, ++ 0, &path); ++ ASSERT_RETURN(ret == 0); ++ ++ snprintf(buspath, sizeof(buspath), "%s/%s/bus", env->root, path); ++ ++ /* ++ * Create a connection with an empty send attach flags, or ++ * all flags except KDBUS_ATTACH_PIDS ++ */ ++ conn = __kdbus_hello(buspath, 0, 0, 0); ++ ASSERT_RETURN(conn == NULL); ++ ASSERT_RETURN(errno == ECONNREFUSED); ++ ++ conn = __kdbus_hello(buspath, 0, ++ _KDBUS_ATTACH_ALL & ~KDBUS_ATTACH_PIDS, ++ _KDBUS_ATTACH_ALL); ++ ASSERT_RETURN(conn == NULL); ++ ASSERT_RETURN(errno == ECONNREFUSED); ++ ++ /* The following should succeed */ ++ conn = __kdbus_hello(buspath, 0, KDBUS_ATTACH_PIDS, 0); ++ ASSERT_RETURN(conn); ++ kdbus_conn_free(conn); ++ ++ conn = __kdbus_hello(buspath, 0, _KDBUS_ATTACH_ALL, 0); ++ ASSERT_RETURN(conn); ++ ++ ret = kdbus_conn_update_attach_flags(conn, ++ _KDBUS_ATTACH_ALL & ++ ~KDBUS_ATTACH_PIDS, ++ _KDBUS_ATTACH_ALL); ++ ASSERT_RETURN(ret == -EINVAL); ++ ++ ret = kdbus_conn_update_attach_flags(conn, 0, ++ _KDBUS_ATTACH_ALL); ++ ASSERT_RETURN(ret == -EINVAL); ++ ++ /* Now we want only KDBUS_ATTACH_PIDS */ ++ ret = kdbus_conn_update_attach_flags(conn, ++ KDBUS_ATTACH_PIDS, 0); ++ ASSERT_RETURN(ret == 0); ++ ++ kdbus_conn_free(conn); ++ free(path); ++ free(busname); ++ close(control_fd); ++ ++ ++ /* ++ * Create bus with 0 as ATTACH flags, the bus does not ++ * require any attach flags ++ */ ++ ++ control_fd = open(control_path, O_RDWR); ++ ASSERT_RETURN(control_fd >= 0); ++ ++ busname = unique_name("test-peer-flags-bus"); ++ ASSERT_RETURN(busname); ++ ++ ret = kdbus_create_bus(control_fd, busname, 0, 0, &path); ++ ASSERT_RETURN(ret == 0); ++ ++ snprintf(buspath, sizeof(buspath), "%s/%s/bus", env->root, path); ++ ++ /* Bus is open it does not require any send attach flags */ ++ conn = __kdbus_hello(buspath, 0, 0, 0); ++ ASSERT_RETURN(conn); ++ kdbus_conn_free(conn); ++ ++ conn = __kdbus_hello(buspath, 0, _KDBUS_ATTACH_ALL, 0); ++ ASSERT_RETURN(conn); ++ ++ ret = kdbus_conn_update_attach_flags(conn, 0, 0); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = kdbus_conn_update_attach_flags(conn, KDBUS_ATTACH_CREDS, 0); ++ ASSERT_RETURN(ret == 0); ++ ++ kdbus_conn_free(conn); ++ free(path); ++ free(busname); ++ close(control_fd); ++ ++ return 0; ++} ++ ++static int kdbus_test_peers_info(struct kdbus_test_env *env) ++{ ++ int ret; ++ int control_fd; ++ char *path; ++ char *busname; ++ unsigned int i = 0; ++ uint64_t offset = 0; ++ char buspath[2048]; ++ char control_path[2048]; ++ uint64_t attach_flags_mask; ++ struct kdbus_item *item; ++ struct kdbus_info *info; ++ struct kdbus_conn *conn; ++ struct kdbus_conn *reader; ++ unsigned long long attach_count = 0; ++ ++ snprintf(control_path, sizeof(control_path), ++ "%s/control", env->root); ++ ++ attach_flags_mask = 0; ++ ret = kdbus_sysfs_set_parameter_mask(env->mask_param_path, ++ attach_flags_mask); ++ ASSERT_RETURN(ret == 0); ++ ++ control_fd = open(control_path, O_RDWR); ++ ASSERT_RETURN(control_fd >= 0); ++ ++ busname = unique_name("test-peers-info-bus"); ++ ASSERT_RETURN(busname); ++ ++ ret = kdbus_create_bus(control_fd, busname, _KDBUS_ATTACH_ALL, ++ 0, &path); ++ ASSERT_RETURN(ret == 0); ++ ++ snprintf(buspath, sizeof(buspath), "%s/%s/bus", env->root, path); ++ ++ /* Create connections with the appropriate flags */ ++ conn = __kdbus_hello(buspath, 0, _KDBUS_ATTACH_ALL, 0); ++ ASSERT_RETURN(conn); ++ ++ reader = __kdbus_hello(buspath, 0, _KDBUS_ATTACH_ALL, 0); ++ ASSERT_RETURN(reader); ++ ++ ret = kdbus_conn_info(reader, conn->id, NULL, ++ _KDBUS_ATTACH_ALL, &offset); ++ ASSERT_RETURN(ret == 0); ++ ++ info = (struct kdbus_info *)(reader->buf + offset); ++ ASSERT_RETURN(info->id == conn->id); ++ ++ /* all attach flags are masked, no metadata */ ++ KDBUS_ITEM_FOREACH(item, info, items) ++ i++; ++ ++ ASSERT_RETURN(i == 0); ++ ++ kdbus_free(reader, offset); ++ ++ /* Set the mask to _KDBUS_ATTACH_ANY */ ++ attach_flags_mask = _KDBUS_ATTACH_ANY; ++ ret = kdbus_sysfs_set_parameter_mask(env->mask_param_path, ++ attach_flags_mask); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = kdbus_conn_info(reader, conn->id, NULL, ++ _KDBUS_ATTACH_ALL, &offset); ++ ASSERT_RETURN(ret == 0); ++ ++ info = (struct kdbus_info *)(reader->buf + offset); ++ ASSERT_RETURN(info->id == conn->id); ++ ++ attach_count = 0; ++ KDBUS_ITEM_FOREACH(item, info, items) ++ attach_count += item->type; ++ ++ /* ++ * All flags have been returned except for: ++ * KDBUS_ITEM_TIMESTAMP and ++ * KDBUS_ITEM_OWNED_NAME we do not own any name. ++ */ ++ ASSERT_RETURN(attach_count == (KDBUS_TEST_ITEMS_SUM - ++ KDBUS_ITEM_OWNED_NAME - ++ KDBUS_ITEM_TIMESTAMP)); ++ ++ kdbus_free(reader, offset); ++ ++ /* Request only OWNED names */ ++ ret = kdbus_conn_info(reader, conn->id, NULL, ++ KDBUS_ATTACH_NAMES, &offset); ++ ASSERT_RETURN(ret == 0); ++ ++ info = (struct kdbus_info *)(reader->buf + offset); ++ ASSERT_RETURN(info->id == conn->id); ++ ++ attach_count = 0; ++ KDBUS_ITEM_FOREACH(item, info, items) ++ attach_count += item->type; ++ ++ /* we should not get any metadata since we do not own names */ ++ ASSERT_RETURN(attach_count == 0); ++ ++ kdbus_free(reader, offset); ++ ++ kdbus_conn_free(conn); ++ kdbus_conn_free(reader); ++ ++ return 0; ++} ++ ++/** ++ * @kdbus_mask_param: kdbus module mask parameter (system-wide) ++ * @requested_meta: The bus owner metadata that we want ++ * @expected_items: The returned KDBUS_ITEMS_* sum. Used to ++ * validate the returned metadata items ++ */ ++static int kdbus_cmp_bus_creator_metadata(struct kdbus_test_env *env, ++ struct kdbus_conn *conn, ++ uint64_t kdbus_mask_param, ++ uint64_t requested_meta, ++ unsigned long expected_items) ++{ ++ int ret; ++ uint64_t offset = 0; ++ struct kdbus_info *info; ++ struct kdbus_item *item; ++ unsigned long attach_count = 0; ++ ++ ret = kdbus_sysfs_set_parameter_mask(env->mask_param_path, ++ kdbus_mask_param); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = kdbus_bus_creator_info(conn, requested_meta, &offset); ++ ASSERT_RETURN(ret == 0); ++ ++ info = (struct kdbus_info *)(conn->buf + offset); ++ ++ KDBUS_ITEM_FOREACH(item, info, items) ++ attach_count += item->type; ++ ++ ASSERT_RETURN(attach_count == expected_items); ++ ++ ret = kdbus_free(conn, offset); ++ ASSERT_RETURN(ret == 0); ++ ++ return 0; ++} ++ ++static int kdbus_test_bus_creator_info(struct kdbus_test_env *env) ++{ ++ int ret; ++ int control_fd; ++ char *path; ++ char *busname; ++ char buspath[2048]; ++ char control_path[2048]; ++ uint64_t attach_flags_mask; ++ struct kdbus_conn *conn; ++ unsigned long expected_items = 0; ++ ++ snprintf(control_path, sizeof(control_path), ++ "%s/control", env->root); ++ ++ control_fd = open(control_path, O_RDWR); ++ ASSERT_RETURN(control_fd >= 0); ++ ++ busname = unique_name("test-peers-info-bus"); ++ ASSERT_RETURN(busname); ++ ++ /* ++ * Now the bus allows us to see all its KDBUS_ATTACH_* ++ * items ++ */ ++ ret = kdbus_create_bus(control_fd, busname, 0, ++ _KDBUS_ATTACH_ALL, &path); ++ ASSERT_RETURN(ret == 0); ++ ++ snprintf(buspath, sizeof(buspath), "%s/%s/bus", env->root, path); ++ ++ conn = __kdbus_hello(buspath, 0, 0, 0); ++ ASSERT_RETURN(conn); ++ ++ /* ++ * Start with a kdbus module mask set to _KDBUS_ATTACH_ANY ++ */ ++ attach_flags_mask = _KDBUS_ATTACH_ANY; ++ ++ /* ++ * All flags will be returned except for: ++ * KDBUS_ITEM_TIMESTAMP ++ * KDBUS_ITEM_OWNED_NAME ++ * KDBUS_ITEM_CONN_DESCRIPTION ++ * ++ * An extra flags is always returned KDBUS_ITEM_MAKE_NAME ++ * which contains the bus name ++ */ ++ expected_items = KDBUS_TEST_ITEMS_SUM + KDBUS_ITEM_MAKE_NAME; ++ expected_items -= KDBUS_ITEM_TIMESTAMP + ++ KDBUS_ITEM_OWNED_NAME + ++ KDBUS_ITEM_CONN_DESCRIPTION; ++ ret = kdbus_cmp_bus_creator_metadata(env, conn, ++ attach_flags_mask, ++ _KDBUS_ATTACH_ALL, ++ expected_items); ++ ASSERT_RETURN(ret == 0); ++ ++ /* ++ * We should have: ++ * KDBUS_ITEM_PIDS + KDBUS_ITEM_CREDS + KDBUS_ITEM_MAKE_NAME ++ */ ++ expected_items = KDBUS_ITEM_PIDS + KDBUS_ITEM_CREDS + ++ KDBUS_ITEM_MAKE_NAME; ++ ret = kdbus_cmp_bus_creator_metadata(env, conn, ++ attach_flags_mask, ++ KDBUS_ATTACH_PIDS | ++ KDBUS_ATTACH_CREDS, ++ expected_items); ++ ASSERT_RETURN(ret == 0); ++ ++ /* KDBUS_ITEM_MAKE_NAME is always returned */ ++ expected_items = KDBUS_ITEM_MAKE_NAME; ++ ret = kdbus_cmp_bus_creator_metadata(env, conn, ++ attach_flags_mask, ++ 0, expected_items); ++ ASSERT_RETURN(ret == 0); ++ ++ /* ++ * Restrict kdbus system-wide mask to KDBUS_ATTACH_PIDS ++ */ ++ ++ attach_flags_mask = KDBUS_ATTACH_PIDS; ++ ++ /* ++ * We should have: ++ * KDBUS_ITEM_PIDS + KDBUS_ITEM_MAKE_NAME ++ */ ++ expected_items = KDBUS_ITEM_PIDS + KDBUS_ITEM_MAKE_NAME; ++ ret = kdbus_cmp_bus_creator_metadata(env, conn, ++ attach_flags_mask, ++ _KDBUS_ATTACH_ALL, ++ expected_items); ++ ASSERT_RETURN(ret == 0); ++ ++ ++ /* system-wide mask to 0 */ ++ attach_flags_mask = 0; ++ ++ /* we should only see: KDBUS_ITEM_MAKE_NAME */ ++ expected_items = KDBUS_ITEM_MAKE_NAME; ++ ret = kdbus_cmp_bus_creator_metadata(env, conn, ++ attach_flags_mask, ++ _KDBUS_ATTACH_ALL, ++ expected_items); ++ ASSERT_RETURN(ret == 0); ++ ++ kdbus_conn_free(conn); ++ free(path); ++ free(busname); ++ close(control_fd); ++ ++ ++ /* ++ * A new bus that hides all its owner metadata ++ */ ++ ++ control_fd = open(control_path, O_RDWR); ++ ASSERT_RETURN(control_fd >= 0); ++ ++ busname = unique_name("test-peers-info-bus"); ++ ASSERT_RETURN(busname); ++ ++ ret = kdbus_create_bus(control_fd, busname, 0, 0, &path); ++ ASSERT_RETURN(ret == 0); ++ ++ snprintf(buspath, sizeof(buspath), "%s/%s/bus", env->root, path); ++ ++ conn = __kdbus_hello(buspath, 0, 0, 0); ++ ASSERT_RETURN(conn); ++ ++ /* ++ * Start with a kdbus module mask set to _KDBUS_ATTACH_ANY ++ */ ++ attach_flags_mask = _KDBUS_ATTACH_ANY; ++ ++ /* ++ * We only get the KDBUS_ITEM_MAKE_NAME ++ */ ++ expected_items = KDBUS_ITEM_MAKE_NAME; ++ ret = kdbus_cmp_bus_creator_metadata(env, conn, ++ attach_flags_mask, ++ _KDBUS_ATTACH_ALL, ++ expected_items); ++ ASSERT_RETURN(ret == 0); ++ ++ /* ++ * We still get only kdbus_ITEM_MAKE_NAME ++ */ ++ attach_flags_mask = 0; ++ expected_items = KDBUS_ITEM_MAKE_NAME; ++ ret = kdbus_cmp_bus_creator_metadata(env, conn, ++ attach_flags_mask, ++ _KDBUS_ATTACH_ALL, ++ expected_items); ++ ASSERT_RETURN(ret == 0); ++ ++ kdbus_conn_free(conn); ++ free(path); ++ free(busname); ++ close(control_fd); ++ ++ ++ /* ++ * A new bus that shows only the PID and CREDS metadata ++ * of the bus owner. ++ */ ++ control_fd = open(control_path, O_RDWR); ++ ASSERT_RETURN(control_fd >= 0); ++ ++ busname = unique_name("test-peers-info-bus"); ++ ASSERT_RETURN(busname); ++ ++ ret = kdbus_create_bus(control_fd, busname, 0, ++ KDBUS_ATTACH_PIDS| ++ KDBUS_ATTACH_CREDS, &path); ++ ASSERT_RETURN(ret == 0); ++ ++ snprintf(buspath, sizeof(buspath), "%s/%s/bus", env->root, path); ++ ++ conn = __kdbus_hello(buspath, 0, 0, 0); ++ ASSERT_RETURN(conn); ++ ++ /* ++ * Start with a kdbus module mask set to _KDBUS_ATTACH_ANY ++ */ ++ attach_flags_mask = _KDBUS_ATTACH_ANY; ++ ++ /* ++ * We should have: ++ * KDBUS_ITEM_PIDS + KDBUS_ITEM_CREDS + KDBUS_ITEM_MAKE_NAME ++ */ ++ expected_items = KDBUS_ITEM_PIDS + KDBUS_ITEM_CREDS + ++ KDBUS_ITEM_MAKE_NAME; ++ ret = kdbus_cmp_bus_creator_metadata(env, conn, ++ attach_flags_mask, ++ _KDBUS_ATTACH_ALL, ++ expected_items); ++ ASSERT_RETURN(ret == 0); ++ ++ expected_items = KDBUS_ITEM_CREDS + KDBUS_ITEM_MAKE_NAME; ++ ret = kdbus_cmp_bus_creator_metadata(env, conn, ++ attach_flags_mask, ++ KDBUS_ATTACH_CREDS, ++ expected_items); ++ ASSERT_RETURN(ret == 0); ++ ++ /* KDBUS_ITEM_MAKE_NAME is always returned */ ++ expected_items = KDBUS_ITEM_MAKE_NAME; ++ ret = kdbus_cmp_bus_creator_metadata(env, conn, ++ attach_flags_mask, ++ 0, expected_items); ++ ASSERT_RETURN(ret == 0); ++ ++ /* ++ * Restrict kdbus system-wide mask to KDBUS_ATTACH_PIDS ++ */ ++ ++ attach_flags_mask = KDBUS_ATTACH_PIDS; ++ /* ++ * We should have: ++ * KDBUS_ITEM_PIDS + KDBUS_ITEM_MAKE_NAME ++ */ ++ expected_items = KDBUS_ITEM_PIDS + KDBUS_ITEM_MAKE_NAME; ++ ret = kdbus_cmp_bus_creator_metadata(env, conn, ++ attach_flags_mask, ++ _KDBUS_ATTACH_ALL, ++ expected_items); ++ ASSERT_RETURN(ret == 0); ++ ++ /* No KDBUS_ATTACH_CREDS */ ++ expected_items = KDBUS_ITEM_MAKE_NAME; ++ ret = kdbus_cmp_bus_creator_metadata(env, conn, ++ attach_flags_mask, ++ KDBUS_ATTACH_CREDS, ++ expected_items); ++ ASSERT_RETURN(ret == 0); ++ ++ /* system-wide mask to 0 */ ++ attach_flags_mask = 0; ++ ++ /* we should only see: KDBUS_ITEM_MAKE_NAME */ ++ expected_items = KDBUS_ITEM_MAKE_NAME; ++ ret = kdbus_cmp_bus_creator_metadata(env, conn, ++ attach_flags_mask, ++ _KDBUS_ATTACH_ALL, ++ expected_items); ++ ASSERT_RETURN(ret == 0); ++ ++ ++ kdbus_conn_free(conn); ++ free(path); ++ free(busname); ++ close(control_fd); ++ ++ return 0; ++} ++ ++int kdbus_test_attach_flags(struct kdbus_test_env *env) ++{ ++ int ret; ++ uint64_t flags_mask; ++ uint64_t old_kdbus_flags_mask; ++ ++ /* We need CAP_DAC_OVERRIDE to overwrite the kdbus mask */ ++ ret = test_is_capable(CAP_DAC_OVERRIDE, -1); ++ ASSERT_RETURN(ret >= 0); ++ ++ /* no enough privileges, SKIP test */ ++ if (!ret) ++ return TEST_SKIP; ++ ++ /* ++ * We need to be able to write to ++ * "/sys/module/kdbus/parameters/attach_flags_mask" ++ * perhaps we are unprvileged/privileged in its userns ++ */ ++ ret = access(env->mask_param_path, W_OK); ++ if (ret < 0) { ++ kdbus_printf("--- access() '%s' failed: %d (%m)\n", ++ env->mask_param_path, -errno); ++ return TEST_SKIP; ++ } ++ ++ ret = kdbus_sysfs_get_parameter_mask(env->mask_param_path, ++ &old_kdbus_flags_mask); ++ ASSERT_RETURN(ret == 0); ++ ++ /* setup the right KDBUS_TEST_ITEMS_SUM */ ++ if (!config_auditsyscall_is_enabled()) ++ KDBUS_TEST_ITEMS_SUM -= KDBUS_ITEM_AUDIT; ++ ++ if (!config_cgroups_is_enabled()) ++ KDBUS_TEST_ITEMS_SUM -= KDBUS_ITEM_CGROUP; ++ ++ if (!config_security_is_enabled()) ++ KDBUS_TEST_ITEMS_SUM -= KDBUS_ITEM_SECLABEL; ++ ++ /* ++ * Test the connection creation attach flags ++ */ ++ ret = kdbus_test_peers_creation(env); ++ /* Restore previous kdbus mask */ ++ kdbus_sysfs_set_parameter_mask(env->mask_param_path, ++ old_kdbus_flags_mask); ++ ASSERT_RETURN(ret == 0); ++ ++ /* ++ * Test the CONN_INFO attach flags ++ */ ++ ret = kdbus_test_peers_info(env); ++ /* Restore previous kdbus mask */ ++ kdbus_sysfs_set_parameter_mask(env->mask_param_path, ++ old_kdbus_flags_mask); ++ ASSERT_RETURN(ret == 0); ++ ++ /* ++ * Test the Bus creator info and its attach flags ++ */ ++ ret = kdbus_test_bus_creator_info(env); ++ /* Restore previous kdbus mask */ ++ kdbus_sysfs_set_parameter_mask(env->mask_param_path, ++ old_kdbus_flags_mask); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = kdbus_sysfs_get_parameter_mask(env->mask_param_path, ++ &flags_mask); ++ ASSERT_RETURN(ret == 0 && old_kdbus_flags_mask == flags_mask); ++ ++ return TEST_OK; ++} +diff --git a/tools/testing/selftests/kdbus/test-benchmark.c b/tools/testing/selftests/kdbus/test-benchmark.c +new file mode 100644 +index 000000000000..8a9744b00508 +--- /dev/null ++++ b/tools/testing/selftests/kdbus/test-benchmark.c +@@ -0,0 +1,451 @@ ++#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/mman.h> ++#include <sys/socket.h> ++#include <math.h> ++ ++#include "kdbus-api.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 bool use_memfd = true; /* transmit memfd? */ ++static bool compare_uds = false; /* unix-socket comparison? */ ++static 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; ++ uint64_t latency_avg; ++ uint64_t latency_ssquares; ++}; ++ ++static struct stats stats; ++ ++static void reset_stats(void) ++{ ++ stats.count = 0; ++ stats.latency_acc = 0; ++ stats.latency_low = UINT64_MAX; ++ stats.latency_high = 0; ++ stats.latency_avg = 0; ++ stats.latency_ssquares = 0; ++} ++ ++static void dump_stats(bool is_uds) ++{ ++ if (stats.count > 0) { ++ kdbus_printf("stats %s: %'llu packets processed, latency (nsecs) min/max/avg/dev %'7llu // %'7llu // %'7llu // %'7.f\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_avg, ++ sqrt(stats.latency_ssquares / stats.count)); ++ } else { ++ kdbus_printf("*** no packets received. bus stuck?\n"); ++ } ++} ++ ++static void add_stats(uint64_t prev) ++{ ++ uint64_t diff, latency_avg_prev; ++ ++ diff = now(CLOCK_THREAD_CPUTIME_ID) - prev; ++ ++ stats.count++; ++ stats.latency_acc += diff; ++ ++ /* see Welford62 */ ++ latency_avg_prev = stats.latency_avg; ++ stats.latency_avg = stats.latency_acc / stats.count; ++ stats.latency_ssquares += (diff - latency_avg_prev) * (diff - stats.latency_avg); ++ ++ 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) ++{ ++ struct kdbus_cmd_send cmd = {}; ++ int memfd = -1; ++ int ret; ++ ++ if (use_memfd) { ++ uint64_t now_ns = now(CLOCK_THREAD_CPUTIME_ID); ++ 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; ++ } ++ ++ cmd.size = sizeof(cmd); ++ cmd.msg_address = (uintptr_t)kdbus_msg; ++ ++ ret = kdbus_cmd_send(conn->fd, &cmd); ++ ASSERT_RETURN_VAL(ret == 0, ret); ++ ++ close(memfd); ++ ++ return 0; ++} ++ ++static int ++handle_echo_reply(struct kdbus_conn *conn, uint64_t send_ns) ++{ ++ int ret; ++ struct kdbus_cmd_recv recv = { .size = sizeof(recv) }; ++ struct kdbus_msg *msg; ++ const struct kdbus_item *item; ++ bool has_memfd = false; ++ ++ ret = kdbus_cmd_recv(conn->fd, &recv); ++ if (ret == -EAGAIN) ++ return ret; ++ ++ ASSERT_RETURN_VAL(ret == 0, ret); ++ ++ if (!use_memfd) ++ goto out; ++ ++ msg = (struct kdbus_msg *)(conn->buf + recv.msg.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.msg.offset); ++ ASSERT_RETURN_VAL(ret == 0, -errno); ++ ++ return 0; ++} ++ ++static int 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, ++ _KDBUS_ATTACH_ALL, ++ 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(CLOCK_THREAD_CPUTIME_ID); ++ reset_stats(); ++ ++ send_ns = now(CLOCK_THREAD_CPUTIME_ID); ++ 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(CLOCK_THREAD_CPUTIME_ID); ++ ret = send_echo_request(conn_b, conn_a->id, ++ kdbus_msg, ++ memfd_cached_offset); ++ ASSERT_RETURN(ret == 0); ++ } ++ ++ now_ns = now(CLOCK_THREAD_CPUTIME_ID); ++ 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(CLOCK_THREAD_CPUTIME_ID); ++ reset_stats(); ++ ++ send_ns = now(CLOCK_THREAD_CPUTIME_ID); ++ 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(CLOCK_THREAD_CPUTIME_ID); ++ ret = write(uds[0], buf, sizeof(buf)); ++ ASSERT_RETURN(ret == sizeof(buf)); ++ } ++ ++ now_ns = now(CLOCK_THREAD_CPUTIME_ID); ++ 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; ++} ++ ++int kdbus_test_benchmark(struct kdbus_test_env *env) ++{ ++ use_memfd = true; ++ attach_none = false; ++ compare_uds = false; ++ return benchmark(env); ++} ++ ++int kdbus_test_benchmark_nomemfds(struct kdbus_test_env *env) ++{ ++ use_memfd = false; ++ attach_none = false; ++ compare_uds = false; ++ return benchmark(env); ++} ++ ++int kdbus_test_benchmark_uds(struct kdbus_test_env *env) ++{ ++ use_memfd = false; ++ attach_none = true; ++ compare_uds = true; ++ return benchmark(env); ++} +diff --git a/tools/testing/selftests/kdbus/test-bus.c b/tools/testing/selftests/kdbus/test-bus.c +new file mode 100644 +index 000000000000..762fb30397d4 +--- /dev/null ++++ b/tools/testing/selftests/kdbus/test-bus.c +@@ -0,0 +1,175 @@ ++#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/mman.h> ++#include <stdbool.h> ++ ++#include "kdbus-api.h" ++#include "kdbus-util.h" ++#include "kdbus-enum.h" ++#include "kdbus-test.h" ++ ++static struct kdbus_item *kdbus_get_item(struct kdbus_info *info, ++ uint64_t type) ++{ ++ struct kdbus_item *item; ++ ++ KDBUS_ITEM_FOREACH(item, info, items) ++ if (item->type == type) ++ return item; ++ ++ return NULL; ++} ++ ++static int test_bus_creator_info(const char *bus_path) ++{ ++ int ret; ++ uint64_t offset; ++ struct kdbus_conn *conn; ++ struct kdbus_info *info; ++ struct kdbus_item *item; ++ char *tmp, *busname; ++ ++ /* extract the bus-name from @bus_path */ ++ tmp = strdup(bus_path); ++ ASSERT_RETURN(tmp); ++ busname = strrchr(tmp, '/'); ++ ASSERT_RETURN(busname); ++ *busname = 0; ++ busname = strrchr(tmp, '/'); ++ ASSERT_RETURN(busname); ++ ++busname; ++ ++ conn = kdbus_hello(bus_path, 0, NULL, 0); ++ ASSERT_RETURN(conn); ++ ++ ret = kdbus_bus_creator_info(conn, _KDBUS_ATTACH_ALL, &offset); ++ ASSERT_RETURN(ret == 0); ++ ++ info = (struct kdbus_info *)(conn->buf + offset); ++ ++ item = kdbus_get_item(info, KDBUS_ITEM_MAKE_NAME); ++ ASSERT_RETURN(item); ++ ASSERT_RETURN(!strcmp(item->str, busname)); ++ ++ ret = kdbus_free(conn, offset); ++ ASSERT_RETURN_VAL(ret == 0, ret); ++ ++ free(tmp); ++ kdbus_conn_free(conn); ++ return 0; ++} ++ ++int kdbus_test_bus_make(struct kdbus_test_env *env) ++{ ++ struct { ++ struct kdbus_cmd cmd; ++ ++ /* 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], *name; ++ int ret, control_fd2; ++ uid_t uid; ++ ++ name = unique_name(""); ++ ASSERT_RETURN(name); ++ ++ snprintf(s, sizeof(s), "%s/control", env->root); ++ env->control_fd = open(s, O_RDWR|O_CLOEXEC); ++ ASSERT_RETURN(env->control_fd >= 0); ++ ++ control_fd2 = open(s, O_RDWR|O_CLOEXEC); ++ ASSERT_RETURN(control_fd2 >= 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.cmd.size = sizeof(struct kdbus_cmd) + ++ sizeof(bus_make.bs) + bus_make.n_size; ++ ret = kdbus_cmd_bus_make(env->control_fd, &bus_make.cmd); ++ ASSERT_RETURN(ret == -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.cmd.size = sizeof(struct kdbus_cmd) + ++ sizeof(bus_make.bs) + bus_make.n_size; ++ ret = kdbus_cmd_bus_make(env->control_fd, &bus_make.cmd); ++ ASSERT_RETURN(ret == -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.cmd.size = sizeof(struct kdbus_cmd) + ++ sizeof(bus_make.bs) + bus_make.n_size; ++ ret = kdbus_cmd_bus_make(env->control_fd, &bus_make.cmd); ++ ASSERT_RETURN(ret == -EINVAL); ++ ++ /* create a new bus */ ++ snprintf(bus_make.name, sizeof(bus_make.name), "%u-%s-1", uid, name); ++ bus_make.n_size = KDBUS_ITEM_HEADER_SIZE + strlen(bus_make.name) + 1; ++ bus_make.cmd.size = sizeof(struct kdbus_cmd) + ++ sizeof(bus_make.bs) + bus_make.n_size; ++ ret = kdbus_cmd_bus_make(env->control_fd, &bus_make.cmd); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = kdbus_cmd_bus_make(control_fd2, &bus_make.cmd); ++ ASSERT_RETURN(ret == -EEXIST); ++ ++ snprintf(s, sizeof(s), "%s/%u-%s-1/bus", env->root, uid, name); ++ 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, even though a different ++ * bus name is used ++ */ ++ snprintf(bus_make.name, sizeof(bus_make.name), "%u-%s-2", uid, name); ++ bus_make.n_size = KDBUS_ITEM_HEADER_SIZE + strlen(bus_make.name) + 1; ++ bus_make.cmd.size = sizeof(struct kdbus_cmd) + ++ sizeof(bus_make.bs) + bus_make.n_size; ++ ret = kdbus_cmd_bus_make(env->control_fd, &bus_make.cmd); ++ ASSERT_RETURN(ret == -EBADFD); ++ ++ /* create a new bus, with different fd and different bus name */ ++ snprintf(bus_make.name, sizeof(bus_make.name), "%u-%s-2", uid, name); ++ bus_make.n_size = KDBUS_ITEM_HEADER_SIZE + strlen(bus_make.name) + 1; ++ bus_make.cmd.size = sizeof(struct kdbus_cmd) + ++ sizeof(bus_make.bs) + bus_make.n_size; ++ ret = kdbus_cmd_bus_make(control_fd2, &bus_make.cmd); ++ ASSERT_RETURN(ret == 0); ++ ++ close(control_fd2); ++ free(name); ++ ++ return TEST_OK; ++} +diff --git a/tools/testing/selftests/kdbus/test-chat.c b/tools/testing/selftests/kdbus/test-chat.c +new file mode 100644 +index 000000000000..71a92d8b7c85 +--- /dev/null ++++ b/tools/testing/selftests/kdbus/test-chat.c +@@ -0,0 +1,122 @@ ++#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 <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_list(conn_b, KDBUS_LIST_UNIQUE | ++ KDBUS_LIST_NAMES | ++ KDBUS_LIST_QUEUED | ++ KDBUS_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_list(conn_b, KDBUS_LIST_UNIQUE | ++ KDBUS_LIST_NAMES | ++ KDBUS_LIST_QUEUED | ++ KDBUS_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; ++} +diff --git a/tools/testing/selftests/kdbus/test-connection.c b/tools/testing/selftests/kdbus/test-connection.c +new file mode 100644 +index 000000000000..5c2bf3511daa +--- /dev/null ++++ b/tools/testing/selftests/kdbus/test-connection.c +@@ -0,0 +1,616 @@ ++#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/types.h> ++#include <sys/capability.h> ++#include <sys/mman.h> ++#include <sys/syscall.h> ++#include <sys/wait.h> ++#include <stdbool.h> ++ ++#include "kdbus-api.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_free cmd_free = {}; ++ struct kdbus_cmd_hello hello; ++ int fd, ret; ++ ++ memset(&hello, 0, sizeof(hello)); ++ ++ fd = open(env->buspath, O_RDWR|O_CLOEXEC); ++ ASSERT_RETURN(fd >= 0); ++ ++ hello.flags = KDBUS_HELLO_ACCEPT_FD; ++ hello.attach_flags_send = _KDBUS_ATTACH_ALL; ++ hello.attach_flags_recv = _KDBUS_ATTACH_ALL; ++ hello.size = sizeof(struct kdbus_cmd_hello); ++ hello.pool_size = POOL_SIZE; ++ ++ /* an unaligned hello must result in -EFAULT */ ++ ret = kdbus_cmd_hello(fd, (struct kdbus_cmd_hello *) ((char *) &hello + 1)); ++ ASSERT_RETURN(ret == -EFAULT); ++ ++ /* a size of 0 must return EMSGSIZE */ ++ hello.size = 1; ++ hello.flags = KDBUS_HELLO_ACCEPT_FD; ++ hello.attach_flags_send = _KDBUS_ATTACH_ALL; ++ ret = kdbus_cmd_hello(fd, &hello); ++ ASSERT_RETURN(ret == -EINVAL); ++ ++ hello.size = sizeof(struct kdbus_cmd_hello); ++ ++ /* check faulty flags */ ++ hello.flags = 1ULL << 32; ++ hello.attach_flags_send = _KDBUS_ATTACH_ALL; ++ ret = kdbus_cmd_hello(fd, &hello); ++ ASSERT_RETURN(ret == -EINVAL); ++ ++ /* check for faulty pool sizes */ ++ hello.pool_size = 0; ++ hello.flags = KDBUS_HELLO_ACCEPT_FD; ++ hello.attach_flags_send = _KDBUS_ATTACH_ALL; ++ ret = kdbus_cmd_hello(fd, &hello); ++ ASSERT_RETURN(ret == -EINVAL); ++ ++ hello.pool_size = 4097; ++ hello.attach_flags_send = _KDBUS_ATTACH_ALL; ++ ret = kdbus_cmd_hello(fd, &hello); ++ ASSERT_RETURN(ret == -EINVAL); ++ ++ hello.pool_size = POOL_SIZE; ++ ++ /* ++ * The connection created by the core requires ALL meta flags ++ * to be sent. An attempt to send less than that should result in ++ * -ECONNREFUSED. ++ */ ++ hello.attach_flags_send = _KDBUS_ATTACH_ALL & ~KDBUS_ATTACH_TIMESTAMP; ++ ret = kdbus_cmd_hello(fd, &hello); ++ ASSERT_RETURN(ret == -ECONNREFUSED); ++ ++ hello.attach_flags_send = _KDBUS_ATTACH_ALL; ++ hello.offset = (__u64)-1; ++ ++ /* success test */ ++ ret = kdbus_cmd_hello(fd, &hello); ++ ASSERT_RETURN(ret == 0); ++ ++ /* The kernel should have returned some items */ ++ ASSERT_RETURN(hello.offset != (__u64)-1); ++ cmd_free.size = sizeof(cmd_free); ++ cmd_free.offset = hello.offset; ++ ret = kdbus_cmd_free(fd, &cmd_free); ++ 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 = kdbus_cmd_hello(fd, &hello); ++ ASSERT_RETURN(ret == -EINVAL); ++ ++ close(fd); ++ ++ return TEST_OK; ++} ++ ++int kdbus_test_byebye(struct kdbus_test_env *env) ++{ ++ struct kdbus_conn *conn; ++ struct kdbus_cmd_recv cmd_recv = { .size = sizeof(cmd_recv) }; ++ struct kdbus_cmd cmd_byebye = { .size = sizeof(cmd_byebye) }; ++ 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 = kdbus_cmd_byebye(conn->fd, &cmd_byebye); ++ ASSERT_RETURN(ret == -EBUSY); ++ ++ /* receive the message */ ++ ret = kdbus_cmd_recv(conn->fd, &cmd_recv); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = kdbus_free(conn, cmd_recv.msg.offset); ++ ASSERT_RETURN(ret == 0); ++ ++ /* and try again */ ++ ret = kdbus_cmd_byebye(conn->fd, &cmd_byebye); ++ ASSERT_RETURN(ret == 0); ++ ++ /* a 2nd try should result in -ECONNRESET */ ++ ret = kdbus_cmd_byebye(conn->fd, &cmd_byebye); ++ ASSERT_RETURN(ret == -ECONNRESET); ++ ++ kdbus_conn_free(conn); ++ ++ return TEST_OK; ++} ++ ++/* Get only the first item */ ++static struct kdbus_item *kdbus_get_item(struct kdbus_info *info, ++ uint64_t type) ++{ ++ struct kdbus_item *item; ++ ++ KDBUS_ITEM_FOREACH(item, info, items) ++ if (item->type == type) ++ return item; ++ ++ return NULL; ++} ++ ++static unsigned int kdbus_count_item(struct kdbus_info *info, ++ uint64_t type) ++{ ++ unsigned int i = 0; ++ const struct kdbus_item *item; ++ ++ KDBUS_ITEM_FOREACH(item, info, items) ++ if (item->type == type) ++ i++; ++ ++ return i; ++} ++ ++static int kdbus_fuzz_conn_info(struct kdbus_test_env *env, int capable) ++{ ++ int ret; ++ unsigned int cnt = 0; ++ uint64_t offset = 0; ++ uint64_t kdbus_flags_mask; ++ struct kdbus_info *info; ++ struct kdbus_conn *conn; ++ struct kdbus_conn *privileged; ++ const struct kdbus_item *item; ++ uint64_t valid_flags_set; ++ uint64_t invalid_flags_set; ++ uint64_t valid_flags = KDBUS_ATTACH_NAMES | ++ KDBUS_ATTACH_CREDS | ++ KDBUS_ATTACH_PIDS | ++ KDBUS_ATTACH_CONN_DESCRIPTION; ++ ++ uint64_t invalid_flags = KDBUS_ATTACH_NAMES | ++ KDBUS_ATTACH_CREDS | ++ KDBUS_ATTACH_PIDS | ++ KDBUS_ATTACH_CAPS | ++ KDBUS_ATTACH_CGROUP | ++ KDBUS_ATTACH_CONN_DESCRIPTION; ++ ++ struct kdbus_creds cached_creds; ++ uid_t ruid, euid, suid; ++ gid_t rgid, egid, sgid; ++ ++ getresuid(&ruid, &euid, &suid); ++ getresgid(&rgid, &egid, &sgid); ++ ++ cached_creds.uid = ruid; ++ cached_creds.euid = euid; ++ cached_creds.suid = suid; ++ cached_creds.fsuid = ruid; ++ ++ cached_creds.gid = rgid; ++ cached_creds.egid = egid; ++ cached_creds.sgid = sgid; ++ cached_creds.fsgid = rgid; ++ ++ struct kdbus_pids cached_pids = { ++ .pid = getpid(), ++ .tid = syscall(SYS_gettid), ++ .ppid = getppid(), ++ }; ++ ++ ret = kdbus_sysfs_get_parameter_mask(env->mask_param_path, ++ &kdbus_flags_mask); ++ ASSERT_RETURN(ret == 0); ++ ++ valid_flags_set = valid_flags & kdbus_flags_mask; ++ invalid_flags_set = invalid_flags & kdbus_flags_mask; ++ ++ ret = kdbus_conn_info(env->conn, env->conn->id, NULL, ++ valid_flags, &offset); ++ ASSERT_RETURN(ret == 0); ++ ++ info = (struct kdbus_info *)(env->conn->buf + offset); ++ ASSERT_RETURN(info->id == env->conn->id); ++ ++ /* We do not have any well-known name */ ++ item = kdbus_get_item(info, KDBUS_ITEM_NAME); ++ ASSERT_RETURN(item == NULL); ++ ++ item = kdbus_get_item(info, KDBUS_ITEM_CONN_DESCRIPTION); ++ if (valid_flags_set & KDBUS_ATTACH_CONN_DESCRIPTION) { ++ ASSERT_RETURN(item); ++ } else { ++ ASSERT_RETURN(item == NULL); ++ } ++ ++ kdbus_free(env->conn, offset); ++ ++ conn = kdbus_hello(env->buspath, 0, NULL, 0); ++ ASSERT_RETURN(conn); ++ ++ privileged = kdbus_hello(env->buspath, 0, NULL, 0); ++ ASSERT_RETURN(privileged); ++ ++ ret = kdbus_conn_info(conn, conn->id, NULL, valid_flags, &offset); ++ ASSERT_RETURN(ret == 0); ++ ++ info = (struct kdbus_info *)(conn->buf + offset); ++ ASSERT_RETURN(info->id == conn->id); ++ ++ /* We do not have any well-known name */ ++ item = kdbus_get_item(info, KDBUS_ITEM_NAME); ++ ASSERT_RETURN(item == NULL); ++ ++ cnt = kdbus_count_item(info, KDBUS_ITEM_CREDS); ++ if (valid_flags_set & KDBUS_ATTACH_CREDS) { ++ ASSERT_RETURN(cnt == 1); ++ ++ item = kdbus_get_item(info, KDBUS_ITEM_CREDS); ++ ASSERT_RETURN(item); ++ ++ /* Compare received items with cached creds */ ++ ASSERT_RETURN(memcmp(&item->creds, &cached_creds, ++ sizeof(struct kdbus_creds)) == 0); ++ } else { ++ ASSERT_RETURN(cnt == 0); ++ } ++ ++ item = kdbus_get_item(info, KDBUS_ITEM_PIDS); ++ if (valid_flags_set & KDBUS_ATTACH_PIDS) { ++ ASSERT_RETURN(item); ++ ++ /* Compare item->pids with cached PIDs */ ++ ASSERT_RETURN(item->pids.pid == cached_pids.pid && ++ item->pids.tid == cached_pids.tid && ++ item->pids.ppid == cached_pids.ppid); ++ } else { ++ ASSERT_RETURN(item == NULL); ++ } ++ ++ /* We did not request KDBUS_ITEM_CAPS */ ++ item = kdbus_get_item(info, KDBUS_ITEM_CAPS); ++ ASSERT_RETURN(item == NULL); ++ ++ kdbus_free(conn, offset); ++ ++ ret = kdbus_name_acquire(conn, "com.example.a", NULL); ++ ASSERT_RETURN(ret >= 0); ++ ++ ret = kdbus_conn_info(conn, conn->id, NULL, valid_flags, &offset); ++ ASSERT_RETURN(ret == 0); ++ ++ info = (struct kdbus_info *)(conn->buf + offset); ++ ASSERT_RETURN(info->id == conn->id); ++ ++ item = kdbus_get_item(info, KDBUS_ITEM_OWNED_NAME); ++ if (valid_flags_set & KDBUS_ATTACH_NAMES) { ++ ASSERT_RETURN(item && !strcmp(item->name.name, "com.example.a")); ++ } else { ++ ASSERT_RETURN(item == NULL); ++ } ++ ++ kdbus_free(conn, offset); ++ ++ ret = kdbus_conn_info(conn, 0, "com.example.a", valid_flags, &offset); ++ ASSERT_RETURN(ret == 0); ++ ++ info = (struct kdbus_info *)(conn->buf + offset); ++ ASSERT_RETURN(info->id == conn->id); ++ ++ kdbus_free(conn, offset); ++ ++ /* does not have the necessary caps to drop to unprivileged */ ++ if (!capable) ++ goto continue_test; ++ ++ ret = RUN_UNPRIVILEGED(UNPRIV_UID, UNPRIV_GID, ({ ++ ret = kdbus_conn_info(conn, conn->id, NULL, ++ valid_flags, &offset); ++ ASSERT_EXIT(ret == 0); ++ ++ info = (struct kdbus_info *)(conn->buf + offset); ++ ASSERT_EXIT(info->id == conn->id); ++ ++ if (valid_flags_set & KDBUS_ATTACH_NAMES) { ++ item = kdbus_get_item(info, KDBUS_ITEM_OWNED_NAME); ++ ASSERT_EXIT(item && ++ strcmp(item->name.name, ++ "com.example.a") == 0); ++ } ++ ++ if (valid_flags_set & KDBUS_ATTACH_CREDS) { ++ item = kdbus_get_item(info, KDBUS_ITEM_CREDS); ++ ASSERT_EXIT(item); ++ ++ /* Compare received items with cached creds */ ++ ASSERT_EXIT(memcmp(&item->creds, &cached_creds, ++ sizeof(struct kdbus_creds)) == 0); ++ } ++ ++ if (valid_flags_set & KDBUS_ATTACH_PIDS) { ++ item = kdbus_get_item(info, KDBUS_ITEM_PIDS); ++ ASSERT_EXIT(item); ++ ++ /* ++ * Compare item->pids with cached pids of ++ * privileged one. ++ * ++ * cmd_info will always return cached pids. ++ */ ++ ASSERT_EXIT(item->pids.pid == cached_pids.pid && ++ item->pids.tid == cached_pids.tid); ++ } ++ ++ kdbus_free(conn, offset); ++ ++ /* ++ * Use invalid_flags and make sure that userspace ++ * do not play with us. ++ */ ++ ret = kdbus_conn_info(conn, conn->id, NULL, ++ invalid_flags, &offset); ++ ASSERT_EXIT(ret == 0); ++ ++ /* ++ * Make sure that we return only one creds item and ++ * it points to the cached creds. ++ */ ++ cnt = kdbus_count_item(info, KDBUS_ITEM_CREDS); ++ if (invalid_flags_set & KDBUS_ATTACH_CREDS) { ++ ASSERT_EXIT(cnt == 1); ++ ++ item = kdbus_get_item(info, KDBUS_ITEM_CREDS); ++ ASSERT_EXIT(item); ++ ++ /* Compare received items with cached creds */ ++ ASSERT_EXIT(memcmp(&item->creds, &cached_creds, ++ sizeof(struct kdbus_creds)) == 0); ++ } else { ++ ASSERT_EXIT(cnt == 0); ++ } ++ ++ if (invalid_flags_set & KDBUS_ATTACH_PIDS) { ++ cnt = kdbus_count_item(info, KDBUS_ITEM_PIDS); ++ ASSERT_EXIT(cnt == 1); ++ ++ item = kdbus_get_item(info, KDBUS_ITEM_PIDS); ++ ASSERT_EXIT(item); ++ ++ /* Compare item->pids with cached pids */ ++ ASSERT_EXIT(item->pids.pid == cached_pids.pid && ++ item->pids.tid == cached_pids.tid); ++ } ++ ++ cnt = kdbus_count_item(info, KDBUS_ITEM_CGROUP); ++ if (invalid_flags_set & KDBUS_ATTACH_CGROUP) { ++ ASSERT_EXIT(cnt == 1); ++ } else { ++ ASSERT_EXIT(cnt == 0); ++ } ++ ++ cnt = kdbus_count_item(info, KDBUS_ITEM_CAPS); ++ if (invalid_flags_set & KDBUS_ATTACH_CAPS) { ++ ASSERT_EXIT(cnt == 1); ++ } else { ++ ASSERT_EXIT(cnt == 0); ++ } ++ ++ kdbus_free(conn, offset); ++ }), ++ ({ 0; })); ++ ASSERT_RETURN(ret == 0); ++ ++continue_test: ++ ++ /* A second name */ ++ ret = kdbus_name_acquire(conn, "com.example.b", NULL); ++ ASSERT_RETURN(ret >= 0); ++ ++ ret = kdbus_conn_info(conn, conn->id, NULL, valid_flags, &offset); ++ ASSERT_RETURN(ret == 0); ++ ++ info = (struct kdbus_info *)(conn->buf + offset); ++ ASSERT_RETURN(info->id == conn->id); ++ ++ cnt = kdbus_count_item(info, KDBUS_ITEM_OWNED_NAME); ++ if (valid_flags_set & KDBUS_ATTACH_NAMES) { ++ ASSERT_RETURN(cnt == 2); ++ } else { ++ ASSERT_RETURN(cnt == 0); ++ } ++ ++ kdbus_free(conn, offset); ++ ++ ASSERT_RETURN(ret == 0); ++ ++ return 0; ++} ++ ++int kdbus_test_conn_info(struct kdbus_test_env *env) ++{ ++ int ret; ++ int have_caps; ++ 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.attach_flags = 0; ++ buf.cmd_info.id = env->conn->id; ++ ++ ret = kdbus_conn_info(env->conn, env->conn->id, NULL, 0, NULL); ++ 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 = kdbus_cmd_conn_info(env->conn->fd, (struct kdbus_cmd_info *) &buf); ++ ASSERT_RETURN(ret == -EINVAL); ++ ++ /* Pass a non existent name */ ++ ret = kdbus_conn_info(env->conn, 0, "non.existent.name", 0, NULL); ++ ASSERT_RETURN(ret == -ESRCH); ++ ++ if (!all_uids_gids_are_mapped()) ++ return TEST_SKIP; ++ ++ /* Test for caps here, so we run the previous test */ ++ have_caps = test_is_capable(CAP_SETUID, CAP_SETGID, -1); ++ ASSERT_RETURN(have_caps >= 0); ++ ++ ret = kdbus_fuzz_conn_info(env, have_caps); ++ ASSERT_RETURN(ret == 0); ++ ++ /* Now if we have skipped some tests then let the user know */ ++ if (!have_caps) ++ return TEST_SKIP; ++ ++ return TEST_OK; ++} ++ ++int kdbus_test_conn_update(struct kdbus_test_env *env) ++{ ++ 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); ++ ++ found = kdbus_item_in_message(msg, KDBUS_ITEM_TIMESTAMP); ++ ASSERT_RETURN(found == 1); ++ ++ kdbus_msg_free(msg); ++ ++ /* ++ * 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_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); ++ ++ found = kdbus_item_in_message(msg, KDBUS_ITEM_TIMESTAMP); ++ ASSERT_RETURN(found == 0); ++ ++ /* Provide a bogus attach_flags value */ ++ ret = kdbus_conn_update_attach_flags(conn, ++ _KDBUS_ATTACH_ALL + 1, ++ _KDBUS_ATTACH_ALL); ++ ASSERT_RETURN(ret == -EINVAL); ++ ++ kdbus_msg_free(msg); ++ ++ kdbus_conn_free(conn); ++ ++ return TEST_OK; ++} ++ ++int kdbus_test_writable_pool(struct kdbus_test_env *env) ++{ ++ struct kdbus_cmd_free cmd_free = {}; ++ 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_send = _KDBUS_ATTACH_ALL; ++ hello.attach_flags_recv = _KDBUS_ATTACH_ALL; ++ hello.size = sizeof(struct kdbus_cmd_hello); ++ hello.pool_size = POOL_SIZE; ++ hello.offset = (__u64)-1; ++ ++ /* success test */ ++ ret = kdbus_cmd_hello(fd, &hello); ++ ASSERT_RETURN(ret == 0); ++ ++ /* The kernel should have returned some items */ ++ ASSERT_RETURN(hello.offset != (__u64)-1); ++ cmd_free.size = sizeof(cmd_free); ++ cmd_free.offset = hello.offset; ++ ret = kdbus_cmd_free(fd, &cmd_free); ++ 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; ++} +diff --git a/tools/testing/selftests/kdbus/test-daemon.c b/tools/testing/selftests/kdbus/test-daemon.c +new file mode 100644 +index 000000000000..8bc2386190d9 +--- /dev/null ++++ b/tools/testing/selftests/kdbus/test-daemon.c +@@ -0,0 +1,65 @@ ++#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 <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; ++} +diff --git a/tools/testing/selftests/kdbus/test-endpoint.c b/tools/testing/selftests/kdbus/test-endpoint.c +new file mode 100644 +index 000000000000..dcc6ab91c4e6 +--- /dev/null ++++ b/tools/testing/selftests/kdbus/test-endpoint.c +@@ -0,0 +1,341 @@ ++#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/capability.h> ++#include <sys/wait.h> ++#include <stdbool.h> ++ ++#include "kdbus-api.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 = kdbus_cmd_match_add(conn->fd, &buf.cmd); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int create_endpoint(const char *buspath, uid_t uid, const char *name, ++ uint64_t flags) ++{ ++ struct { ++ struct kdbus_cmd cmd; ++ ++ /* 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", uid, 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.cmd.flags = flags; ++ ep_make.cmd.size = sizeof(ep_make.cmd) + ep_make.name.size; ++ ++ ret = kdbus_cmd_endpoint_make(fd, &ep_make.cmd); ++ if (ret < 0) { ++ kdbus_printf("error creating endpoint: %d (%m)\n", ret); ++ return ret; ++ } ++ ++ return fd; ++} ++ ++static int unpriv_test_custom_ep(const char *buspath) ++{ ++ int ret, ep_fd1, ep_fd2; ++ char *ep1, *ep2, *tmp1, *tmp2; ++ ++ tmp1 = strdup(buspath); ++ tmp2 = strdup(buspath); ++ ASSERT_RETURN(tmp1 && tmp2); ++ ++ ret = asprintf(&ep1, "%s/%u-%s", dirname(tmp1), getuid(), "apps1"); ++ ASSERT_RETURN(ret >= 0); ++ ++ ret = asprintf(&ep2, "%s/%u-%s", dirname(tmp2), getuid(), "apps2"); ++ ASSERT_RETURN(ret >= 0); ++ ++ free(tmp1); ++ free(tmp2); ++ ++ /* endpoint only accessible to current uid */ ++ ep_fd1 = create_endpoint(buspath, getuid(), "apps1", 0); ++ ASSERT_RETURN(ep_fd1 >= 0); ++ ++ /* endpoint world accessible */ ++ ep_fd2 = create_endpoint(buspath, getuid(), "apps2", ++ KDBUS_MAKE_ACCESS_WORLD); ++ ASSERT_RETURN(ep_fd2 >= 0); ++ ++ ret = RUN_UNPRIVILEGED(UNPRIV_UID, UNPRIV_UID, ({ ++ int ep_fd; ++ struct kdbus_conn *ep_conn; ++ ++ /* ++ * Make sure that we are not able to create custom ++ * endpoints ++ */ ++ ep_fd = create_endpoint(buspath, getuid(), ++ "unpriv_costum_ep", 0); ++ ASSERT_EXIT(ep_fd == -EPERM); ++ ++ /* ++ * Endpoint "apps1" only accessible to same users, ++ * that own the endpoint. Access denied by VFS ++ */ ++ ep_conn = kdbus_hello(ep1, 0, NULL, 0); ++ ASSERT_EXIT(!ep_conn && errno == EACCES); ++ ++ /* Endpoint "apps2" world accessible */ ++ ep_conn = kdbus_hello(ep2, 0, NULL, 0); ++ ASSERT_EXIT(ep_conn); ++ ++ kdbus_conn_free(ep_conn); ++ ++ _exit(EXIT_SUCCESS); ++ }), ++ ({ 0; })); ++ ASSERT_RETURN(ret == 0); ++ ++ close(ep_fd1); ++ close(ep_fd2); ++ free(ep1); ++ free(ep2); ++ ++ return 0; ++} ++ ++static int update_endpoint(int fd, const char *name) ++{ ++ int len = strlen(name) + 1; ++ struct { ++ struct kdbus_cmd cmd; ++ ++ /* 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.cmd.size = sizeof(ep_update); ++ ++ ret = kdbus_cmd_endpoint_update(fd, &ep_update.cmd); ++ if (ret < 0) { ++ 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; ++ struct kdbus_conn *reader; ++ 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, getuid(), fake_ep, 0); ++ ASSERT_RETURN(ret == -ENAMETOOLONG); ++ ++ /* Try to create a custom endpoint with a different uid */ ++ ret = create_endpoint(env->buspath, getuid() + 1, "foobar", 0); ++ ASSERT_RETURN(ret == -EINVAL); ++ ++ /* create a custom endpoint, and open a connection on it */ ++ ep_fd = create_endpoint(env->buspath, getuid(), "foo", 0); ++ 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); ++ ++ /* Register a connection that listen to broadcasts */ ++ reader = kdbus_hello(ep, 0, NULL, 0); ++ ASSERT_RETURN(reader); ++ ++ /* Register to kernel signals */ ++ ret = kdbus_add_match_id(reader, 0x1, KDBUS_ITEM_ID_ADD, ++ KDBUS_MATCH_ID_ANY); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = kdbus_add_match_id(reader, 0x2, KDBUS_ITEM_ID_REMOVE, ++ KDBUS_MATCH_ID_ANY); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = install_name_add_match(reader, name); ++ ASSERT_RETURN(ret == 0); ++ ++ /* Monitor connections are not supported on custom endpoints */ ++ ep_conn = kdbus_hello(ep, KDBUS_HELLO_MONITOR, NULL, 0); ++ ASSERT_RETURN(!ep_conn && errno == EOPNOTSUPP); ++ ++ 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_conn_info(ep_conn, 0, name, 0, NULL); ++ ASSERT_RETURN(ret == -ESRCH); ++ ++ ret = kdbus_conn_info(ep_conn, 0, "random.crappy.name", 0, NULL); ++ ASSERT_RETURN(ret == -ESRCH); ++ ++ ret = kdbus_conn_info(ep_conn, env->conn->id, NULL, 0, NULL); ++ ASSERT_RETURN(ret == -ENXIO); ++ ++ ret = kdbus_conn_info(ep_conn, 0x0fffffffffffffffULL, NULL, 0, NULL); ++ ASSERT_RETURN(ret == -ENXIO); ++ ++ /* Check that the reader did not receive anything */ ++ ret = kdbus_msg_recv(reader, NULL, NULL); ++ ASSERT_RETURN(ret == -EAGAIN); ++ ++ /* ++ * 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_msg_recv(reader, &msg, NULL); ++ ASSERT_RETURN(ret == 0); ++ ASSERT_RETURN(strcmp(msg->items[0].name_change.name, name) == 0); ++ ++ kdbus_msg_free(msg); ++ ++ ret = kdbus_conn_info(ep_conn, 0, name, 0, NULL); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = kdbus_conn_info(ep_conn, env->conn->id, NULL, 0, NULL); ++ ASSERT_RETURN(ret == 0); ++ ++ /* If we have privileges test custom endpoints */ ++ ret = test_is_capable(CAP_SETUID, CAP_SETGID, -1); ++ ASSERT_RETURN(ret >= 0); ++ ++ /* ++ * All uids/gids are mapped and we have the necessary caps ++ */ ++ if (ret && all_uids_gids_are_mapped()) { ++ ret = unpriv_test_custom_ep(env->buspath); ++ ASSERT_RETURN(ret == 0); ++ } ++ ++ kdbus_conn_free(reader); ++ kdbus_conn_free(ep_conn); ++ close(ep_fd); ++ ++ return TEST_OK; ++} +diff --git a/tools/testing/selftests/kdbus/test-fd.c b/tools/testing/selftests/kdbus/test-fd.c +new file mode 100644 +index 000000000000..2ae0f5ae8fd0 +--- /dev/null ++++ b/tools/testing/selftests/kdbus/test-fd.c +@@ -0,0 +1,789 @@ ++#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/mman.h> ++#include <sys/socket.h> ++#include <sys/wait.h> ++ ++#include "kdbus-api.h" ++#include "kdbus-test.h" ++#include "kdbus-util.h" ++#include "kdbus-enum.h" ++ ++#define KDBUS_MSG_MAX_ITEMS 128 ++#define KDBUS_USER_MAX_CONN 256 ++ ++/* maximum number of inflight fds in a target queue per user */ ++#define KDBUS_CONN_MAX_FDS_PER_USER 16 ++ ++/* maximum number of memfd items per message */ ++#define KDBUS_MSG_MAX_MEMFD_ITEMS 16 ++ ++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_cmd_send cmd = {}; ++ 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); ++ ++ msg->flags |= KDBUS_MSG_SIGNAL; ++ } ++ ++ make_item_memfds(item, memfds_array, memfd_count); ++ ++ cmd.size = sizeof(cmd); ++ cmd.msg_address = (uintptr_t)msg; ++ ++ ret = kdbus_cmd_send(conn->fd, &cmd); ++ if (ret < 0) { ++ 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_cmd_send cmd = {}; ++ 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); ++ ++ 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); ++ ++ msg->flags |= KDBUS_MSG_SIGNAL; ++ } ++ ++ make_item_fds(item, fd_array, fd_count); ++ ++ cmd.size = sizeof(cmd); ++ cmd.msg_address = (uintptr_t)msg; ++ ++ ret = kdbus_cmd_send(conn->fd, &cmd); ++ if (ret < 0) { ++ 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_cmd_send cmd = {}; ++ 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); ++ ++ cmd.size = sizeof(cmd); ++ cmd.msg_address = (uintptr_t)msg; ++ ++ ret = kdbus_cmd_send(conn->fd, &cmd); ++ if (ret < 0) { ++ 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 struct kdbus_msg * ++get_kdbus_msg_with_fd(struct kdbus_conn *conn_src, ++ uint64_t dst_id, uint64_t cookie, int fd) ++{ ++ int ret; ++ uint64_t size; ++ struct kdbus_item *item; ++ struct kdbus_msg *msg; ++ ++ size = sizeof(struct kdbus_msg); ++ if (fd >= 0) ++ size += KDBUS_ITEM_SIZE(sizeof(int)); ++ ++ ret = make_msg_payload_dbus(conn_src->id, dst_id, size, &msg); ++ ASSERT_RETURN_VAL(ret == 0, NULL); ++ ++ msg->cookie = cookie; ++ ++ if (fd >= 0) { ++ item = msg->items; ++ ++ make_item_fds(item, (int *)&fd, 1); ++ } ++ ++ return msg; ++} ++ ++static int kdbus_test_no_fds(struct kdbus_test_env *env, ++ int *fds, int *memfd) ++{ ++ pid_t pid; ++ int ret, status; ++ uint64_t cookie; ++ int connfd1, connfd2; ++ struct kdbus_msg *msg, *msg_sync_reply; ++ struct kdbus_cmd_hello hello; ++ struct kdbus_conn *conn_src, *conn_dst, *conn_dummy; ++ struct kdbus_cmd_send cmd = {}; ++ struct kdbus_cmd_free cmd_free = {}; ++ ++ conn_src = kdbus_hello(env->buspath, 0, NULL, 0); ++ ASSERT_RETURN(conn_src); ++ ++ connfd1 = open(env->buspath, O_RDWR|O_CLOEXEC); ++ ASSERT_RETURN(connfd1 >= 0); ++ ++ connfd2 = open(env->buspath, O_RDWR|O_CLOEXEC); ++ ASSERT_RETURN(connfd2 >= 0); ++ ++ /* ++ * Create connections without KDBUS_HELLO_ACCEPT_FD ++ * to test if send fd operations are blocked ++ */ ++ conn_dst = malloc(sizeof(*conn_dst)); ++ ASSERT_RETURN(conn_dst); ++ ++ conn_dummy = malloc(sizeof(*conn_dummy)); ++ ASSERT_RETURN(conn_dummy); ++ ++ memset(&hello, 0, sizeof(hello)); ++ hello.size = sizeof(struct kdbus_cmd_hello); ++ hello.pool_size = POOL_SIZE; ++ hello.attach_flags_send = _KDBUS_ATTACH_ALL; ++ ++ ret = kdbus_cmd_hello(connfd1, &hello); ++ ASSERT_RETURN(ret == 0); ++ ++ cmd_free.size = sizeof(cmd_free); ++ cmd_free.offset = hello.offset; ++ ret = kdbus_cmd_free(connfd1, &cmd_free); ++ ASSERT_RETURN(ret >= 0); ++ ++ conn_dst->fd = connfd1; ++ conn_dst->id = hello.id; ++ ++ memset(&hello, 0, sizeof(hello)); ++ hello.size = sizeof(struct kdbus_cmd_hello); ++ hello.pool_size = POOL_SIZE; ++ hello.attach_flags_send = _KDBUS_ATTACH_ALL; ++ ++ ret = kdbus_cmd_hello(connfd2, &hello); ++ ASSERT_RETURN(ret == 0); ++ ++ cmd_free.size = sizeof(cmd_free); ++ cmd_free.offset = hello.offset; ++ ret = kdbus_cmd_free(connfd2, &cmd_free); ++ ASSERT_RETURN(ret >= 0); ++ ++ conn_dummy->fd = connfd2; ++ conn_dummy->id = hello.id; ++ ++ conn_dst->buf = mmap(NULL, POOL_SIZE, PROT_READ, ++ MAP_SHARED, connfd1, 0); ++ ASSERT_RETURN(conn_dst->buf != MAP_FAILED); ++ ++ conn_dummy->buf = mmap(NULL, POOL_SIZE, PROT_READ, ++ MAP_SHARED, connfd2, 0); ++ ASSERT_RETURN(conn_dummy->buf != MAP_FAILED); ++ ++ /* ++ * Send fds to connection that do not accept fd passing ++ */ ++ ret = send_fds(conn_src, conn_dst->id, fds, 1); ++ ASSERT_RETURN(ret == -ECOMM); ++ ++ /* ++ * memfd are kdbus payload ++ */ ++ ret = send_memfds(conn_src, conn_dst->id, memfd, 1); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = kdbus_msg_recv_poll(conn_dst, 100, NULL, NULL); ++ ASSERT_RETURN(ret == 0); ++ ++ cookie = time(NULL); ++ ++ pid = fork(); ++ ASSERT_RETURN_VAL(pid >= 0, pid); ++ ++ if (pid == 0) { ++ struct timespec now; ++ ++ /* ++ * A sync send/reply to a connection that do not ++ * accept fds should fail if it contains an fd ++ */ ++ msg_sync_reply = get_kdbus_msg_with_fd(conn_dst, ++ conn_dummy->id, ++ cookie, fds[0]); ++ ASSERT_EXIT(msg_sync_reply); ++ ++ ret = clock_gettime(CLOCK_MONOTONIC_COARSE, &now); ++ ASSERT_EXIT(ret == 0); ++ ++ msg_sync_reply->timeout_ns = now.tv_sec * 1000000000ULL + ++ now.tv_nsec + 100000000ULL; ++ msg_sync_reply->flags = KDBUS_MSG_EXPECT_REPLY; ++ ++ memset(&cmd, 0, sizeof(cmd)); ++ cmd.size = sizeof(cmd); ++ cmd.msg_address = (uintptr_t)msg_sync_reply; ++ cmd.flags = KDBUS_SEND_SYNC_REPLY; ++ ++ ret = kdbus_cmd_send(conn_dst->fd, &cmd); ++ ASSERT_EXIT(ret == -ECOMM); ++ ++ /* ++ * Now send a normal message, but the sync reply ++ * will fail since it contains an fd that the ++ * original sender do not want. ++ * ++ * The original sender will fail with -ETIMEDOUT ++ */ ++ cookie++; ++ ret = kdbus_msg_send_sync(conn_dst, NULL, cookie, ++ KDBUS_MSG_EXPECT_REPLY, ++ 5000000000ULL, 0, conn_src->id, -1); ++ ASSERT_EXIT(ret == -EREMOTEIO); ++ ++ cookie++; ++ ret = kdbus_msg_recv_poll(conn_dst, 100, &msg, NULL); ++ ASSERT_EXIT(ret == 0); ++ ASSERT_EXIT(msg->cookie == cookie); ++ ++ free(msg_sync_reply); ++ kdbus_msg_free(msg); ++ ++ _exit(EXIT_SUCCESS); ++ } ++ ++ ret = kdbus_msg_recv_poll(conn_dummy, 100, NULL, NULL); ++ ASSERT_RETURN(ret == -ETIMEDOUT); ++ ++ cookie++; ++ ret = kdbus_msg_recv_poll(conn_src, 100, &msg, NULL); ++ ASSERT_RETURN(ret == 0 && msg->cookie == cookie); ++ ++ kdbus_msg_free(msg); ++ ++ /* ++ * Try to reply with a kdbus connection handle, this should ++ * fail with -EOPNOTSUPP ++ */ ++ msg_sync_reply = get_kdbus_msg_with_fd(conn_src, ++ conn_dst->id, ++ cookie, conn_dst->fd); ++ ASSERT_RETURN(msg_sync_reply); ++ ++ msg_sync_reply->cookie_reply = cookie; ++ ++ memset(&cmd, 0, sizeof(cmd)); ++ cmd.size = sizeof(cmd); ++ cmd.msg_address = (uintptr_t)msg_sync_reply; ++ ++ ret = kdbus_cmd_send(conn_src->fd, &cmd); ++ ASSERT_RETURN(ret == -EOPNOTSUPP); ++ ++ free(msg_sync_reply); ++ ++ /* ++ * Try to reply with a normal fd, this should fail even ++ * if the response is a sync reply ++ * ++ * From the sender view we fail with -ECOMM ++ */ ++ msg_sync_reply = get_kdbus_msg_with_fd(conn_src, ++ conn_dst->id, ++ cookie, fds[0]); ++ ASSERT_RETURN(msg_sync_reply); ++ ++ msg_sync_reply->cookie_reply = cookie; ++ ++ memset(&cmd, 0, sizeof(cmd)); ++ cmd.size = sizeof(cmd); ++ cmd.msg_address = (uintptr_t)msg_sync_reply; ++ ++ ret = kdbus_cmd_send(conn_src->fd, &cmd); ++ ASSERT_RETURN(ret == -ECOMM); ++ ++ free(msg_sync_reply); ++ ++ /* ++ * Resend another normal message and check if the queue ++ * is clear ++ */ ++ cookie++; ++ ret = kdbus_msg_send(conn_src, NULL, cookie, 0, 0, 0, ++ conn_dst->id); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = waitpid(pid, &status, 0); ++ ASSERT_RETURN_VAL(ret >= 0, ret); ++ ++ kdbus_conn_free(conn_dummy); ++ kdbus_conn_free(conn_dst); ++ kdbus_conn_free(conn_src); ++ ++ return (status == EXIT_SUCCESS) ? TEST_OK : TEST_ERR; ++} ++ ++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_CONN_MAX_FDS_PER_USER + 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_CONN_MAX_FDS_PER_USER + 1; i++) { ++ fds[i] = open("/dev/null", O_RDWR|O_CLOEXEC); ++ ASSERT_RETURN_VAL(fds[i] >= 0, -errno); ++ } ++ ++ /* Send KDBUS_CONN_MAX_FDS_PER_USER with one more fd */ ++ ret = send_fds(conn_src, conn_dst->id, fds, ++ KDBUS_CONN_MAX_FDS_PER_USER + 1); ++ ASSERT_RETURN(ret == -EMFILE); ++ ++ /* Retry with the correct KDBUS_CONN_MAX_FDS_PER_USER */ ++ ret = send_fds(conn_src, conn_dst->id, fds, ++ KDBUS_CONN_MAX_FDS_PER_USER); ++ 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_CONN_MAX_FDS_PER_USER); ++ ++ 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_ITEMS with one more memfd */ ++ ret = send_memfds(conn_src, conn_dst->id, ++ memfds, KDBUS_MSG_MAX_ITEMS + 1); ++ ASSERT_RETURN(ret == -E2BIG); ++ ++ ret = send_memfds(conn_src, conn_dst->id, ++ memfds, KDBUS_MSG_MAX_MEMFD_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_MEMFD_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_MEMFD_ITEMS); ++ ++ kdbus_msg_free(msg); ++ ++ ++ /* ++ * Combine multiple KDBUS_CONN_MAX_FDS_PER_USER+1 fds and ++ * 10 memfds ++ */ ++ ret = send_fds_memfds(conn_src, conn_dst->id, ++ fds, KDBUS_CONN_MAX_FDS_PER_USER + 1, ++ memfds, 10); ++ ASSERT_RETURN(ret == -EMFILE); ++ ++ ret = kdbus_msg_recv(conn_dst, NULL, NULL); ++ ASSERT_RETURN(ret == -EAGAIN); ++ ++ /* ++ * Combine multiple KDBUS_CONN_MAX_FDS_PER_USER fds and ++ * (128 - 1) + 1 memfds, all fds take one item, while each ++ * memfd takes one item ++ */ ++ ret = send_fds_memfds(conn_src, conn_dst->id, ++ fds, KDBUS_CONN_MAX_FDS_PER_USER, ++ memfds, (KDBUS_MSG_MAX_ITEMS - 1) + 1); ++ ASSERT_RETURN(ret == -E2BIG); ++ ++ ret = send_fds_memfds(conn_src, conn_dst->id, ++ fds, KDBUS_CONN_MAX_FDS_PER_USER, ++ memfds, KDBUS_MSG_MAX_MEMFD_ITEMS + 1); ++ ASSERT_RETURN(ret == -E2BIG); ++ ++ ret = kdbus_msg_recv(conn_dst, NULL, NULL); ++ ASSERT_RETURN(ret == -EAGAIN); ++ ++ /* ++ * Send KDBUS_CONN_MAX_FDS_PER_USER fds + ++ * KDBUS_MSG_MAX_MEMFD_ITEMS memfds ++ */ ++ ret = send_fds_memfds(conn_src, conn_dst->id, ++ fds, KDBUS_CONN_MAX_FDS_PER_USER, ++ memfds, KDBUS_MSG_MAX_MEMFD_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_CONN_MAX_FDS_PER_USER + ++ KDBUS_MSG_MAX_MEMFD_ITEMS); ++ ++ kdbus_msg_free(msg); ++ ++ ++ /* ++ * Re-send fds + memfds, close them, but do not receive them ++ * and try to queue more ++ */ ++ ret = send_fds_memfds(conn_src, conn_dst->id, ++ fds, KDBUS_CONN_MAX_FDS_PER_USER, ++ memfds, KDBUS_MSG_MAX_MEMFD_ITEMS); ++ ASSERT_RETURN(ret == 0); ++ ++ /* close old references and get a new ones */ ++ for (i = 0; i < KDBUS_CONN_MAX_FDS_PER_USER + 1; i++) { ++ close(fds[i]); ++ fds[i] = open("/dev/null", O_RDWR|O_CLOEXEC); ++ ASSERT_RETURN_VAL(fds[i] >= 0, -errno); ++ } ++ ++ /* should fail since we have already fds in the queue */ ++ ret = send_fds(conn_src, conn_dst->id, fds, ++ KDBUS_CONN_MAX_FDS_PER_USER); ++ ASSERT_RETURN(ret == -EMFILE); ++ ++ /* This should succeed */ ++ ret = send_memfds(conn_src, conn_dst->id, ++ memfds, KDBUS_MSG_MAX_MEMFD_ITEMS); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = kdbus_msg_recv(conn_dst, &msg, NULL); ++ ASSERT_RETURN(ret == 0); ++ ++ nfds = kdbus_item_get_nfds(msg); ++ ASSERT_RETURN(nfds == KDBUS_CONN_MAX_FDS_PER_USER + ++ KDBUS_MSG_MAX_MEMFD_ITEMS); ++ ++ kdbus_msg_free(msg); ++ ++ ret = kdbus_msg_recv(conn_dst, &msg, NULL); ++ ASSERT_RETURN(ret == 0); ++ ++ nfds = kdbus_item_get_nfds(msg); ++ ASSERT_RETURN(nfds == KDBUS_MSG_MAX_MEMFD_ITEMS); ++ ++ kdbus_msg_free(msg); ++ ++ ret = kdbus_msg_recv(conn_dst, NULL, NULL); ++ ASSERT_RETURN(ret == -EAGAIN); ++ ++ for (i = 0; i < KDBUS_CONN_MAX_FDS_PER_USER + 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; ++ const char *str = "stackenblocken"; ++ const struct kdbus_item *item; ++ struct kdbus_msg *msg; ++ unsigned int i; ++ uint64_t now; ++ int fds_conn[2]; ++ int sock_pair[2]; ++ int fds[2]; ++ int memfd; ++ int ret; ++ ++ now = (uint64_t) time(NULL); ++ ++ /* 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 and memfds to connection that do not accept fds ++ */ ++ ret = kdbus_test_no_fds(env, fds, (int *)&memfd); ++ 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); ++ ++ kdbus_conn_free(conn_src); ++ kdbus_conn_free(conn_dst); ++ ++ return TEST_OK; ++} +diff --git a/tools/testing/selftests/kdbus/test-free.c b/tools/testing/selftests/kdbus/test-free.c +new file mode 100644 +index 000000000000..f666da3e87cc +--- /dev/null ++++ b/tools/testing/selftests/kdbus/test-free.c +@@ -0,0 +1,64 @@ ++#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 <stdbool.h> ++ ++#include "kdbus-api.h" ++#include "kdbus-util.h" ++#include "kdbus-enum.h" ++#include "kdbus-test.h" ++ ++static int sample_ioctl_call(struct kdbus_test_env *env) ++{ ++ int ret; ++ struct kdbus_cmd_list cmd_list = { ++ .flags = KDBUS_LIST_QUEUED, ++ .size = sizeof(cmd_list), ++ }; ++ ++ ret = kdbus_cmd_list(env->conn->fd, &cmd_list); ++ ASSERT_RETURN(ret == 0); ++ ++ /* DON'T FREE THIS SLICE OF MEMORY! */ ++ ++ return TEST_OK; ++} ++ ++int kdbus_test_free(struct kdbus_test_env *env) ++{ ++ int ret; ++ struct kdbus_cmd_free cmd_free = {}; ++ ++ /* free an unallocated buffer */ ++ cmd_free.size = sizeof(cmd_free); ++ cmd_free.flags = 0; ++ cmd_free.offset = 0; ++ ret = kdbus_cmd_free(env->conn->fd, &cmd_free); ++ ASSERT_RETURN(ret == -ENXIO); ++ ++ /* free a buffer out of the pool's bounds */ ++ cmd_free.size = sizeof(cmd_free); ++ cmd_free.offset = POOL_SIZE + 1; ++ ret = kdbus_cmd_free(env->conn->fd, &cmd_free); ++ ASSERT_RETURN(ret == -ENXIO); ++ ++ /* ++ * The user application is responsible for freeing the allocated ++ * memory with the KDBUS_CMD_FREE ioctl, so let's test what happens ++ * if we forget about it. ++ */ ++ ++ ret = sample_ioctl_call(env); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = sample_ioctl_call(env); ++ ASSERT_RETURN(ret == 0); ++ ++ return TEST_OK; ++} +diff --git a/tools/testing/selftests/kdbus/test-match.c b/tools/testing/selftests/kdbus/test-match.c +new file mode 100644 +index 000000000000..2360dc1d76a4 +--- /dev/null ++++ b/tools/testing/selftests/kdbus/test-match.c +@@ -0,0 +1,441 @@ ++#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 <stdbool.h> ++ ++#include "kdbus-api.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 = kdbus_cmd_match_add(env->conn->fd, &buf.cmd); ++ 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); ++ ASSERT_RETURN(conn != NULL); ++ id = conn->id; ++ ++ 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 = kdbus_cmd_match_add(env->conn->fd, &buf.cmd); ++ 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_replace(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; ++ ++ /* add a match to id_add */ ++ ASSERT_RETURN(kdbus_test_match_id_add(env) == TEST_OK); ++ ++ /* do a replace of the match from id_add to id_remove */ ++ memset(&buf, 0, sizeof(buf)); ++ ++ buf.cmd.size = sizeof(buf); ++ buf.cmd.cookie = 0xdeafbeefdeaddead; ++ buf.cmd.flags = KDBUS_MATCH_REPLACE; ++ buf.item.size = sizeof(buf.item); ++ buf.item.type = KDBUS_ITEM_ID_REMOVE; ++ buf.item.chg.id = KDBUS_MATCH_ID_ANY; ++ ++ ret = kdbus_cmd_match_add(env->conn->fd, &buf.cmd); ++ ++ /* create 2nd connection */ ++ conn = kdbus_hello(env->buspath, 0, NULL, 0); ++ ASSERT_RETURN(conn != NULL); ++ id = conn->id; ++ ++ /* 1st connection should _not_ have received a notification */ ++ ret = kdbus_msg_recv(env->conn, &msg, NULL); ++ ASSERT_RETURN(ret != 0); ++ ++ /* remove 2nd connection */ ++ kdbus_conn_free(conn); ++ ++ /* 1st connection should _now_ 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 = kdbus_cmd_match_add(env->conn->fd, &buf.cmd); ++ 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 = kdbus_cmd_match_add(env->conn->fd, &buf.cmd); ++ 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 = kdbus_cmd_match_add(env->conn->fd, &buf.cmd); ++ 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_cmd_send cmd = {}; ++ 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->flags = KDBUS_MSG_SIGNAL; ++ 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); ++ ++ cmd.size = sizeof(cmd); ++ cmd.msg_address = (uintptr_t)msg; ++ ++ ret = kdbus_cmd_send(conn->fd, &cmd); ++ if (ret < 0) { ++ 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 = kdbus_cmd_match_add(env->conn->fd, &buf.cmd); ++ 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; ++} +diff --git a/tools/testing/selftests/kdbus/test-message.c b/tools/testing/selftests/kdbus/test-message.c +new file mode 100644 +index 000000000000..f1615dafb7f1 +--- /dev/null ++++ b/tools/testing/selftests/kdbus/test-message.c +@@ -0,0 +1,731 @@ ++#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 <time.h> ++#include <stdbool.h> ++#include <sys/eventfd.h> ++#include <sys/types.h> ++#include <sys/wait.h> ++ ++#include "kdbus-api.h" ++#include "kdbus-util.h" ++#include "kdbus-enum.h" ++#include "kdbus-test.h" ++ ++/* maximum number of queued messages from the same individual user */ ++#define KDBUS_CONN_MAX_MSGS 256 ++ ++/* maximum number of queued requests waiting for a reply */ ++#define KDBUS_CONN_MAX_REQUESTS_PENDING 128 ++ ++/* maximum message payload size */ ++#define KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE (2 * 1024UL * 1024UL) ++ ++int kdbus_test_message_basic(struct kdbus_test_env *env) ++{ ++ struct kdbus_conn *conn; ++ struct kdbus_conn *sender; ++ struct kdbus_msg *msg; ++ uint64_t cookie = 0x1234abcd5678eeff; ++ uint64_t offset; ++ int ret; ++ ++ sender = kdbus_hello(env->buspath, 0, NULL, 0); ++ ASSERT_RETURN(sender != NULL); ++ ++ /* 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(sender); ++ ASSERT_RETURN(ret == 0); ++ ++ /* send over 1st connection */ ++ ret = kdbus_msg_send(sender, NULL, cookie, 0, 0, 0, ++ KDBUS_DST_ID_BROADCAST); ++ ASSERT_RETURN(ret == 0); ++ ++ /* Make sure that we do not get our own broadcasts */ ++ ret = kdbus_msg_recv(sender, NULL, NULL); ++ ASSERT_RETURN(ret == -EAGAIN); ++ ++ /* ... 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); ++ ++ /* Msgs that expect a reply must have timeout and cookie */ ++ ret = kdbus_msg_send(sender, NULL, 0, KDBUS_MSG_EXPECT_REPLY, ++ 0, 0, conn->id); ++ ASSERT_RETURN(ret == -EINVAL); ++ ++ /* Faked replies with a valid reply cookie are rejected */ ++ ret = kdbus_msg_send_reply(conn, time(NULL) ^ cookie, sender->id); ++ ASSERT_RETURN(ret == -EPERM); ++ ++ ret = kdbus_free(conn, offset); ++ ASSERT_RETURN(ret == 0); ++ ++ kdbus_conn_free(sender); ++ 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 = { ++ .size = sizeof(recv), ++ .flags = KDBUS_RECV_USE_PRIORITY, ++ .priority = requested_prio, ++ }; ++ struct kdbus_msg *msg; ++ int ret; ++ ++ ret = kdbus_cmd_recv(conn->fd, &recv); ++ if (ret < 0) { ++ kdbus_printf("error receiving message: %d (%m)\n", -errno); ++ return ret; ++ } ++ ++ msg = (struct kdbus_msg *)(conn->buf + recv.msg.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.msg.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) == -EAGAIN); ++ 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; ++} ++ ++static int kdbus_test_notify_kernel_quota(struct kdbus_test_env *env) ++{ ++ int ret; ++ unsigned int i; ++ struct kdbus_conn *conn; ++ struct kdbus_conn *reader; ++ struct kdbus_msg *msg = NULL; ++ struct kdbus_cmd_recv recv = { .size = sizeof(recv) }; ++ ++ reader = kdbus_hello(env->buspath, 0, NULL, 0); ++ ASSERT_RETURN(reader); ++ ++ conn = kdbus_hello(env->buspath, 0, NULL, 0); ++ ASSERT_RETURN(conn); ++ ++ /* Register for ID signals */ ++ ret = kdbus_add_match_id(reader, 0x1, KDBUS_ITEM_ID_ADD, ++ KDBUS_MATCH_ID_ANY); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = kdbus_add_match_id(reader, 0x2, KDBUS_ITEM_ID_REMOVE, ++ KDBUS_MATCH_ID_ANY); ++ ASSERT_RETURN(ret == 0); ++ ++ /* Each iteration two notifications: add and remove ID */ ++ for (i = 0; i < KDBUS_CONN_MAX_MSGS / 2; i++) { ++ struct kdbus_conn *notifier; ++ ++ notifier = kdbus_hello(env->buspath, 0, NULL, 0); ++ ASSERT_RETURN(notifier); ++ ++ kdbus_conn_free(notifier); ++ } ++ ++ /* ++ * Now the reader queue is full with kernel notfications, ++ * but as a user we still have room to push our messages. ++ */ ++ ret = kdbus_msg_send(conn, NULL, 0xdeadbeef, 0, 0, 0, reader->id); ++ ASSERT_RETURN(ret == 0); ++ ++ /* More ID kernel notifications that will be lost */ ++ kdbus_conn_free(conn); ++ ++ conn = kdbus_hello(env->buspath, 0, NULL, 0); ++ ASSERT_RETURN(conn); ++ ++ kdbus_conn_free(conn); ++ ++ /* ++ * We lost only 3 packets since only signal msgs are ++ * accounted. The connection ID add/remove notification ++ */ ++ ret = kdbus_cmd_recv(reader->fd, &recv); ++ ASSERT_RETURN(ret == 0); ++ ASSERT_RETURN(recv.return_flags & KDBUS_RECV_RETURN_DROPPED_MSGS); ++ ASSERT_RETURN(recv.dropped_msgs == 3); ++ ++ msg = (struct kdbus_msg *)(reader->buf + recv.msg.offset); ++ kdbus_msg_free(msg); ++ ++ /* Read our queue */ ++ for (i = 0; i < KDBUS_CONN_MAX_MSGS - 1; i++) { ++ memset(&recv, 0, sizeof(recv)); ++ recv.size = sizeof(recv); ++ ++ ret = kdbus_cmd_recv(reader->fd, &recv); ++ ASSERT_RETURN(ret == 0); ++ ASSERT_RETURN(!(recv.return_flags & ++ KDBUS_RECV_RETURN_DROPPED_MSGS)); ++ ++ msg = (struct kdbus_msg *)(reader->buf + recv.msg.offset); ++ kdbus_msg_free(msg); ++ } ++ ++ ret = kdbus_msg_recv(reader, NULL, NULL); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = kdbus_msg_recv(reader, NULL, NULL); ++ ASSERT_RETURN(ret == -EAGAIN); ++ ++ kdbus_conn_free(reader); ++ ++ return 0; ++} ++ ++/* Return the number of message successfully sent */ ++static int kdbus_fill_conn_queue(struct kdbus_conn *conn_src, ++ uint64_t dst_id, ++ unsigned int max_msgs) ++{ ++ unsigned int i; ++ uint64_t cookie = 0; ++ size_t size; ++ struct kdbus_cmd_send cmd = {}; ++ struct kdbus_msg *msg; ++ int ret; ++ ++ size = sizeof(struct kdbus_msg); ++ msg = malloc(size); ++ ASSERT_RETURN_VAL(msg, -ENOMEM); ++ ++ memset(msg, 0, size); ++ msg->size = size; ++ msg->src_id = conn_src->id; ++ msg->dst_id = dst_id; ++ msg->payload_type = KDBUS_PAYLOAD_DBUS; ++ ++ cmd.size = sizeof(cmd); ++ cmd.msg_address = (uintptr_t)msg; ++ ++ for (i = 0; i < max_msgs; i++) { ++ msg->cookie = cookie++; ++ ret = kdbus_cmd_send(conn_src->fd, &cmd); ++ if (ret < 0) ++ break; ++ } ++ ++ free(msg); ++ ++ return i; ++} ++ ++static int kdbus_test_activator_quota(struct kdbus_test_env *env) ++{ ++ int ret; ++ unsigned int i; ++ unsigned int activator_msgs_count = 0; ++ uint64_t cookie = time(NULL); ++ struct kdbus_conn *conn; ++ struct kdbus_conn *sender; ++ struct kdbus_conn *activator; ++ struct kdbus_msg *msg; ++ uint64_t flags = KDBUS_NAME_REPLACE_EXISTING; ++ struct kdbus_cmd_recv recv = { .size = sizeof(recv) }; ++ struct kdbus_policy_access access = { ++ .type = KDBUS_POLICY_ACCESS_USER, ++ .id = geteuid(), ++ .access = KDBUS_POLICY_OWN, ++ }; ++ ++ activator = kdbus_hello_activator(env->buspath, "foo.test.activator", ++ &access, 1); ++ ASSERT_RETURN(activator); ++ ++ conn = kdbus_hello(env->buspath, 0, NULL, 0); ++ sender = kdbus_hello(env->buspath, 0, NULL, 0); ++ ASSERT_RETURN(conn || sender); ++ ++ ret = kdbus_list(sender, KDBUS_LIST_NAMES | ++ KDBUS_LIST_UNIQUE | ++ KDBUS_LIST_ACTIVATORS | ++ KDBUS_LIST_QUEUED); ++ ASSERT_RETURN(ret == 0); ++ ++ for (i = 0; i < KDBUS_CONN_MAX_MSGS; i++) { ++ ret = kdbus_msg_send(sender, "foo.test.activator", ++ cookie++, 0, 0, 0, ++ KDBUS_DST_ID_NAME); ++ if (ret < 0) ++ break; ++ activator_msgs_count++; ++ } ++ ++ /* we must have at least sent one message */ ++ ASSERT_RETURN_VAL(i > 0, -errno); ++ ASSERT_RETURN(ret == -ENOBUFS); ++ ++ /* Good, activator queue is full now */ ++ ++ /* ENXIO on direct send (activators can never be addressed by ID) */ ++ ret = kdbus_msg_send(conn, NULL, cookie++, 0, 0, 0, activator->id); ++ ASSERT_RETURN(ret == -ENXIO); ++ ++ /* can't queue more */ ++ ret = kdbus_msg_send(conn, "foo.test.activator", cookie++, ++ 0, 0, 0, KDBUS_DST_ID_NAME); ++ ASSERT_RETURN(ret == -ENOBUFS); ++ ++ /* no match installed, so the broadcast will not inc dropped_msgs */ ++ ret = kdbus_msg_send(sender, NULL, cookie++, 0, 0, 0, ++ KDBUS_DST_ID_BROADCAST); ++ ASSERT_RETURN(ret == 0); ++ ++ /* Check activator queue */ ++ ret = kdbus_cmd_recv(activator->fd, &recv); ++ ASSERT_RETURN(ret == 0); ++ ASSERT_RETURN(recv.dropped_msgs == 0); ++ ++ activator_msgs_count--; ++ ++ msg = (struct kdbus_msg *)(activator->buf + recv.msg.offset); ++ kdbus_msg_free(msg); ++ ++ ++ /* Stage 1) of test check the pool memory quota */ ++ ++ /* Consume the connection pool memory */ ++ for (i = 0; i < KDBUS_CONN_MAX_MSGS; i++) { ++ ret = kdbus_msg_send(sender, NULL, ++ cookie++, 0, 0, 0, conn->id); ++ if (ret < 0) ++ break; ++ } ++ ++ /* consume one message, so later at least one can be moved */ ++ memset(&recv, 0, sizeof(recv)); ++ recv.size = sizeof(recv); ++ ret = kdbus_cmd_recv(conn->fd, &recv); ++ ASSERT_RETURN(ret == 0); ++ ASSERT_RETURN(recv.dropped_msgs == 0); ++ msg = (struct kdbus_msg *)(conn->buf + recv.msg.offset); ++ kdbus_msg_free(msg); ++ ++ /* Try to acquire the name now */ ++ ret = kdbus_name_acquire(conn, "foo.test.activator", &flags); ++ ASSERT_RETURN(ret == 0); ++ ++ /* try to read messages and see if we have lost some */ ++ memset(&recv, 0, sizeof(recv)); ++ recv.size = sizeof(recv); ++ ret = kdbus_cmd_recv(conn->fd, &recv); ++ ASSERT_RETURN(ret == 0); ++ ASSERT_RETURN(recv.dropped_msgs != 0); ++ ++ /* number of dropped msgs < received ones (at least one was moved) */ ++ ASSERT_RETURN(recv.dropped_msgs < activator_msgs_count); ++ ++ /* Deduct the number of dropped msgs from the activator msgs */ ++ activator_msgs_count -= recv.dropped_msgs; ++ ++ msg = (struct kdbus_msg *)(activator->buf + recv.msg.offset); ++ kdbus_msg_free(msg); ++ ++ /* ++ * Release the name and hand it back to activator, now ++ * we should have 'activator_msgs_count' msgs again in ++ * the activator queue ++ */ ++ ret = kdbus_name_release(conn, "foo.test.activator"); ++ ASSERT_RETURN(ret == 0); ++ ++ /* make sure that we got our previous activator msgs */ ++ ret = kdbus_msg_recv(activator, &msg, NULL); ++ ASSERT_RETURN(ret == 0); ++ ASSERT_RETURN(msg->src_id == sender->id); ++ ++ activator_msgs_count--; ++ ++ kdbus_msg_free(msg); ++ ++ ++ /* Stage 2) of test check max message quota */ ++ ++ /* Empty conn queue */ ++ for (i = 0; i < KDBUS_CONN_MAX_MSGS; i++) { ++ ret = kdbus_msg_recv(conn, NULL, NULL); ++ if (ret == -EAGAIN) ++ break; ++ } ++ ++ /* fill queue with max msgs quota */ ++ ret = kdbus_fill_conn_queue(sender, conn->id, KDBUS_CONN_MAX_MSGS); ++ ASSERT_RETURN(ret == KDBUS_CONN_MAX_MSGS); ++ ++ /* This one is lost but it is not accounted */ ++ ret = kdbus_msg_send(sender, NULL, ++ cookie++, 0, 0, 0, conn->id); ++ ASSERT_RETURN(ret == -ENOBUFS); ++ ++ /* Acquire the name again */ ++ ret = kdbus_name_acquire(conn, "foo.test.activator", &flags); ++ ASSERT_RETURN(ret == 0); ++ ++ memset(&recv, 0, sizeof(recv)); ++ recv.size = sizeof(recv); ++ ++ /* ++ * Try to read messages and make sure that we have lost all ++ * the activator messages due to quota checks. Our queue is ++ * already full. ++ */ ++ ret = kdbus_cmd_recv(conn->fd, &recv); ++ ASSERT_RETURN(ret == 0); ++ ASSERT_RETURN(recv.dropped_msgs == activator_msgs_count); ++ ++ msg = (struct kdbus_msg *)(activator->buf + recv.msg.offset); ++ kdbus_msg_free(msg); ++ ++ kdbus_conn_free(sender); ++ kdbus_conn_free(conn); ++ kdbus_conn_free(activator); ++ ++ return 0; ++} ++ ++static int kdbus_test_expected_reply_quota(struct kdbus_test_env *env) ++{ ++ int ret; ++ unsigned int i, n; ++ unsigned int count; ++ uint64_t cookie = 0x1234abcd5678eeff; ++ struct kdbus_conn *conn; ++ struct kdbus_conn *connections[9]; ++ ++ conn = kdbus_hello(env->buspath, 0, NULL, 0); ++ ASSERT_RETURN(conn); ++ ++ for (i = 0; i < 9; i++) { ++ connections[i] = kdbus_hello(env->buspath, 0, NULL, 0); ++ ASSERT_RETURN(connections[i]); ++ } ++ ++ count = 0; ++ /* Send 16 messages to 8 different connections */ ++ for (i = 0; i < 8; i++) { ++ for (n = 0; n < 16; n++) { ++ ret = kdbus_msg_send(conn, NULL, cookie++, ++ KDBUS_MSG_EXPECT_REPLY, ++ 100000000ULL, 0, ++ connections[i]->id); ++ if (ret < 0) ++ break; ++ ++ count++; ++ } ++ } ++ ++ /* ++ * We should have queued at least ++ * KDBUS_CONN_MAX_REQUESTS_PENDING method call ++ */ ++ ASSERT_RETURN(count == KDBUS_CONN_MAX_REQUESTS_PENDING); ++ ++ /* ++ * Now try to send a message to the last connection, ++ * if we have reached KDBUS_CONN_MAX_REQUESTS_PENDING ++ * no further requests are allowed ++ */ ++ ret = kdbus_msg_send(conn, NULL, cookie++, KDBUS_MSG_EXPECT_REPLY, ++ 1000000000ULL, 0, connections[8]->id); ++ ASSERT_RETURN(ret == -EMLINK); ++ ++ for (i = 0; i < 9; i++) ++ kdbus_conn_free(connections[i]); ++ ++ kdbus_conn_free(conn); ++ ++ return 0; ++} ++ ++int kdbus_test_pool_quota(struct kdbus_test_env *env) ++{ ++ struct kdbus_conn *a, *b, *c; ++ struct kdbus_cmd_send cmd = {}; ++ struct kdbus_item *item; ++ struct kdbus_msg *recv_msg; ++ struct kdbus_msg *msg; ++ uint64_t cookie = time(NULL); ++ uint64_t size; ++ unsigned int i; ++ char *payload; ++ int ret; ++ ++ /* just a guard */ ++ if (POOL_SIZE <= KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE || ++ POOL_SIZE % KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE != 0) ++ return 0; ++ ++ payload = calloc(KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE, sizeof(char)); ++ ASSERT_RETURN_VAL(payload, -ENOMEM); ++ ++ a = kdbus_hello(env->buspath, 0, NULL, 0); ++ b = kdbus_hello(env->buspath, 0, NULL, 0); ++ c = kdbus_hello(env->buspath, 0, NULL, 0); ++ ASSERT_RETURN(a && b && c); ++ ++ 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 = a->id; ++ msg->dst_id = c->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)payload; ++ item->vec.size = KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE; ++ item = KDBUS_ITEM_NEXT(item); ++ ++ cmd.size = sizeof(cmd); ++ cmd.msg_address = (uintptr_t)msg; ++ ++ /* ++ * Send 2097248 bytes, a user is only allowed to get 33% of half of ++ * the free space of the pool, the already used space is ++ * accounted as free space ++ */ ++ size += KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE; ++ for (i = size; i < (POOL_SIZE / 2 / 3); i += size) { ++ msg->cookie = cookie++; ++ ++ ret = kdbus_cmd_send(a->fd, &cmd); ++ ASSERT_RETURN_VAL(ret == 0, ret); ++ } ++ ++ /* Try to get more than 33% */ ++ msg->cookie = cookie++; ++ ret = kdbus_cmd_send(a->fd, &cmd); ++ ASSERT_RETURN(ret == -ENOBUFS); ++ ++ /* We still can pass small messages */ ++ ret = kdbus_msg_send(b, NULL, cookie++, 0, 0, 0, c->id); ++ ASSERT_RETURN(ret == 0); ++ ++ for (i = size; i < (POOL_SIZE / 2 / 3); i += size) { ++ ret = kdbus_msg_recv(c, &recv_msg, NULL); ++ ASSERT_RETURN(ret == 0); ++ ASSERT_RETURN(recv_msg->src_id == a->id); ++ ++ kdbus_msg_free(recv_msg); ++ } ++ ++ ret = kdbus_msg_recv(c, &recv_msg, NULL); ++ ASSERT_RETURN(ret == 0); ++ ASSERT_RETURN(recv_msg->src_id == b->id); ++ ++ kdbus_msg_free(recv_msg); ++ ++ ret = kdbus_msg_recv(c, NULL, NULL); ++ ASSERT_RETURN(ret == -EAGAIN); ++ ++ free(msg); ++ free(payload); ++ ++ kdbus_conn_free(c); ++ kdbus_conn_free(b); ++ kdbus_conn_free(a); ++ ++ return 0; ++} ++ ++int kdbus_test_message_quota(struct kdbus_test_env *env) ++{ ++ struct kdbus_conn *a, *b; ++ uint64_t cookie = 0; ++ int ret; ++ int i; ++ ++ ret = kdbus_test_activator_quota(env); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = kdbus_test_notify_kernel_quota(env); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = kdbus_test_pool_quota(env); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = kdbus_test_expected_reply_quota(env); ++ ASSERT_RETURN(ret == 0); ++ ++ a = kdbus_hello(env->buspath, 0, NULL, 0); ++ b = kdbus_hello(env->buspath, 0, NULL, 0); ++ ++ ret = kdbus_fill_conn_queue(b, a->id, KDBUS_CONN_MAX_MSGS); ++ ASSERT_RETURN(ret == KDBUS_CONN_MAX_MSGS); ++ ++ ret = kdbus_msg_send(b, NULL, ++cookie, 0, 0, 0, a->id); ++ ASSERT_RETURN(ret == -ENOBUFS); ++ ++ for (i = 0; i < KDBUS_CONN_MAX_MSGS; ++i) { ++ ret = kdbus_msg_recv(a, NULL, NULL); ++ ASSERT_RETURN(ret == 0); ++ } ++ ++ ret = kdbus_msg_recv(a, NULL, NULL); ++ ASSERT_RETURN(ret == -EAGAIN); ++ ++ ret = kdbus_fill_conn_queue(b, a->id, KDBUS_CONN_MAX_MSGS + 1); ++ ASSERT_RETURN(ret == KDBUS_CONN_MAX_MSGS); ++ ++ ret = kdbus_msg_send(b, NULL, ++cookie, 0, 0, 0, a->id); ++ ASSERT_RETURN(ret == -ENOBUFS); ++ ++ kdbus_conn_free(a); ++ kdbus_conn_free(b); ++ ++ return TEST_OK; ++} ++ ++int kdbus_test_memory_access(struct kdbus_test_env *env) ++{ ++ struct kdbus_conn *a, *b; ++ struct kdbus_cmd_send cmd = {}; ++ struct kdbus_item *item; ++ struct kdbus_msg *msg; ++ uint64_t test_addr = 0; ++ char line[256]; ++ uint64_t size; ++ FILE *f; ++ int ret; ++ ++ /* ++ * Search in /proc/kallsyms for the address of a kernel symbol that ++ * should always be there, regardless of the config. Use that address ++ * in a PAYLOAD_VEC item and make sure it's inaccessible. ++ */ ++ ++ f = fopen("/proc/kallsyms", "r"); ++ if (!f) ++ return TEST_SKIP; ++ ++ while (fgets(line, sizeof(line), f)) { ++ char *s = line; ++ ++ if (!strsep(&s, " ")) ++ continue; ++ ++ if (!strsep(&s, " ")) ++ continue; ++ ++ if (!strncmp(s, "mutex_lock", 10)) { ++ test_addr = strtoull(line, NULL, 16); ++ break; ++ } ++ } ++ ++ fclose(f); ++ ++ if (!test_addr) ++ return TEST_SKIP; ++ ++ a = kdbus_hello(env->buspath, 0, NULL, 0); ++ b = kdbus_hello(env->buspath, 0, NULL, 0); ++ ASSERT_RETURN(a && b); ++ ++ size = sizeof(struct kdbus_msg); ++ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec)); ++ ++ msg = alloca(size); ++ ASSERT_RETURN_VAL(msg, -ENOMEM); ++ ++ memset(msg, 0, size); ++ msg->size = size; ++ msg->src_id = a->id; ++ msg->dst_id = b->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 = test_addr; ++ item->vec.size = sizeof(void*); ++ item = KDBUS_ITEM_NEXT(item); ++ ++ cmd.size = sizeof(cmd); ++ cmd.msg_address = (uintptr_t)msg; ++ ++ ret = kdbus_cmd_send(a->fd, &cmd); ++ ASSERT_RETURN(ret == -EFAULT); ++ ++ kdbus_conn_free(b); ++ kdbus_conn_free(a); ++ ++ return 0; ++} +diff --git a/tools/testing/selftests/kdbus/test-metadata-ns.c b/tools/testing/selftests/kdbus/test-metadata-ns.c +new file mode 100644 +index 000000000000..2cb1d4d2a5be +--- /dev/null ++++ b/tools/testing/selftests/kdbus/test-metadata-ns.c +@@ -0,0 +1,506 @@ ++/* ++ * Test metadata in new namespaces. Even if our tests can run ++ * in a namespaced setup, this test is necessary so we can inspect ++ * metadata on the same kdbusfs but between multiple 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/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 const struct kdbus_creds privileged_creds = {}; ++ ++static const struct kdbus_creds unmapped_creds = { ++ .uid = UNPRIV_UID, ++ .euid = UNPRIV_UID, ++ .suid = UNPRIV_UID, ++ .fsuid = UNPRIV_UID, ++ .gid = UNPRIV_GID, ++ .egid = UNPRIV_GID, ++ .sgid = UNPRIV_GID, ++ .fsgid = UNPRIV_GID, ++}; ++ ++static const struct kdbus_pids unmapped_pids = {}; ++ ++/* Get only the first item */ ++static struct kdbus_item *kdbus_get_item(struct kdbus_msg *msg, ++ uint64_t type) ++{ ++ struct kdbus_item *item; ++ ++ KDBUS_ITEM_FOREACH(item, msg, items) ++ if (item->type == type) ++ return item; ++ ++ return NULL; ++} ++ ++static int kdbus_match_kdbus_creds(struct kdbus_msg *msg, ++ const struct kdbus_creds *expected_creds) ++{ ++ struct kdbus_item *item; ++ ++ item = kdbus_get_item(msg, KDBUS_ITEM_CREDS); ++ ASSERT_RETURN(item); ++ ++ ASSERT_RETURN(memcmp(&item->creds, expected_creds, ++ sizeof(struct kdbus_creds)) == 0); ++ ++ return 0; ++} ++ ++static int kdbus_match_kdbus_pids(struct kdbus_msg *msg, ++ const struct kdbus_pids *expected_pids) ++{ ++ struct kdbus_item *item; ++ ++ item = kdbus_get_item(msg, KDBUS_ITEM_PIDS); ++ ASSERT_RETURN(item); ++ ++ ASSERT_RETURN(memcmp(&item->pids, expected_pids, ++ sizeof(struct kdbus_pids)) == 0); ++ ++ return 0; ++} ++ ++static int __kdbus_clone_userns_test(const char *bus, ++ struct kdbus_conn *conn, ++ uint64_t grandpa_pid, ++ int signal_fd) ++{ ++ int clone_ret; ++ int ret; ++ struct kdbus_msg *msg = NULL; ++ const struct kdbus_item *item; ++ uint64_t cookie = time(NULL) ^ 0xdeadbeef; ++ struct kdbus_conn *unpriv_conn = NULL; ++ struct kdbus_pids parent_pids = { ++ .pid = getppid(), ++ .tid = getppid(), ++ .ppid = grandpa_pid, ++ }; ++ ++ ret = drop_privileges(UNPRIV_UID, UNPRIV_GID); ++ ASSERT_EXIT(ret == 0); ++ ++ unpriv_conn = kdbus_hello(bus, 0, NULL, 0); ++ ASSERT_EXIT(unpriv_conn); ++ ++ ret = kdbus_add_match_empty(unpriv_conn); ++ ASSERT_EXIT(ret == 0); ++ ++ /* ++ * ping privileged connection from this new unprivileged ++ * one ++ */ ++ ++ ret = kdbus_msg_send(unpriv_conn, NULL, cookie, 0, 0, ++ 0, conn->id); ++ ASSERT_EXIT(ret == 0); ++ ++ /* ++ * 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_EXIT(ret == 0); ++ ++ /* Make child privileged in its new userns and run tests */ ++ ++ ret = RUN_CLONE_CHILD(&clone_ret, ++ SIGCHLD | CLONE_NEWUSER | CLONE_NEWPID, ++ ({ 0; /* Clone setup, nothing */ }), ++ ({ ++ eventfd_t event_status = 0; ++ struct kdbus_conn *userns_conn; ++ ++ /* ping connection from the new user namespace */ ++ userns_conn = kdbus_hello(bus, 0, NULL, 0); ++ ASSERT_EXIT(userns_conn); ++ ++ ret = kdbus_add_match_empty(userns_conn); ++ ASSERT_EXIT(ret == 0); ++ ++ cookie++; ++ ret = kdbus_msg_send(userns_conn, NULL, cookie, ++ 0, 0, 0, conn->id); ++ ASSERT_EXIT(ret == 0); ++ ++ /* Parent did send */ ++ ret = eventfd_read(signal_fd, &event_status); ++ ASSERT_RETURN(ret >= 0 && event_status == 1); ++ ++ /* ++ * Receive from privileged connection ++ */ ++ kdbus_printf("Privileged → unprivileged/privileged " ++ "in its userns " ++ "(different userns and pidns):\n"); ++ ret = kdbus_msg_recv_poll(userns_conn, 300, &msg, NULL); ++ ASSERT_EXIT(ret == 0); ++ ASSERT_EXIT(msg->dst_id == userns_conn->id); ++ ++ /* Different namespaces no CAPS */ ++ item = kdbus_get_item(msg, KDBUS_ITEM_CAPS); ++ ASSERT_EXIT(item == NULL); ++ ++ /* uid/gid not mapped, so we have unpriv cached creds */ ++ ret = kdbus_match_kdbus_creds(msg, &unmapped_creds); ++ ASSERT_EXIT(ret == 0); ++ ++ /* ++ * Diffent pid namepsaces. This is the child pidns ++ * so it should not see its parent kdbus_pids ++ */ ++ ret = kdbus_match_kdbus_pids(msg, &unmapped_pids); ++ ASSERT_EXIT(ret == 0); ++ ++ kdbus_msg_free(msg); ++ ++ ++ /* ++ * Receive broadcast from privileged connection ++ */ ++ kdbus_printf("Privileged → unprivileged/privileged " ++ "in its userns " ++ "(different userns and pidns):\n"); ++ ret = kdbus_msg_recv_poll(userns_conn, 300, &msg, NULL); ++ ASSERT_EXIT(ret == 0); ++ ASSERT_EXIT(msg->dst_id == KDBUS_DST_ID_BROADCAST); ++ ++ /* Different namespaces no CAPS */ ++ item = kdbus_get_item(msg, KDBUS_ITEM_CAPS); ++ ASSERT_EXIT(item == NULL); ++ ++ /* uid/gid not mapped, so we have unpriv cached creds */ ++ ret = kdbus_match_kdbus_creds(msg, &unmapped_creds); ++ ASSERT_EXIT(ret == 0); ++ ++ /* ++ * Diffent pid namepsaces. This is the child pidns ++ * so it should not see its parent kdbus_pids ++ */ ++ ret = kdbus_match_kdbus_pids(msg, &unmapped_pids); ++ ASSERT_EXIT(ret == 0); ++ ++ kdbus_msg_free(msg); ++ ++ kdbus_conn_free(userns_conn); ++ }), ++ ({ ++ /* Parent setup map child uid/gid */ ++ ret = userns_map_uid_gid(pid, "0 65534 1", "0 65534 1"); ++ ASSERT_EXIT(ret == 0); ++ }), ++ ({ 0; })); ++ /* Unprivileged was not able to create user namespace */ ++ if (clone_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", UNPRIV_UID); ++ ret = 0; ++ goto out; ++ } ++ ++ ASSERT_EXIT(ret == 0); ++ ++ ++ /* ++ * Receive from privileged connection ++ */ ++ kdbus_printf("\nPrivileged → unprivileged (same namespaces):\n"); ++ ret = kdbus_msg_recv_poll(unpriv_conn, 300, &msg, NULL); ++ ++ ASSERT_EXIT(ret == 0); ++ ASSERT_EXIT(msg->dst_id == unpriv_conn->id); ++ ++ /* will get the privileged creds */ ++ ret = kdbus_match_kdbus_creds(msg, &privileged_creds); ++ ASSERT_EXIT(ret == 0); ++ ++ /* Same pidns so will get the kdbus_pids */ ++ ret = kdbus_match_kdbus_pids(msg, &parent_pids); ++ ASSERT_RETURN(ret == 0); ++ ++ kdbus_msg_free(msg); ++ ++ ++ /* ++ * Receive broadcast from privileged connection ++ */ ++ kdbus_printf("\nPrivileged → unprivileged (same namespaces):\n"); ++ ret = kdbus_msg_recv_poll(unpriv_conn, 300, &msg, NULL); ++ ++ ASSERT_EXIT(ret == 0); ++ ASSERT_EXIT(msg->dst_id == KDBUS_DST_ID_BROADCAST); ++ ++ /* will get the privileged creds */ ++ ret = kdbus_match_kdbus_creds(msg, &privileged_creds); ++ ASSERT_EXIT(ret == 0); ++ ++ ret = kdbus_match_kdbus_pids(msg, &parent_pids); ++ ASSERT_RETURN(ret == 0); ++ ++ kdbus_msg_free(msg); ++ ++out: ++ kdbus_conn_free(unpriv_conn); ++ ++ return ret; ++} ++ ++static int kdbus_clone_userns_test(const char *bus, ++ struct kdbus_conn *conn) ++{ ++ int ret; ++ int status; ++ int efd = -1; ++ pid_t pid, ppid; ++ uint64_t unpriv_conn_id = 0; ++ uint64_t userns_conn_id = 0; ++ struct kdbus_msg *msg; ++ const struct kdbus_item *item; ++ struct kdbus_pids expected_pids; ++ struct kdbus_conn *monitor = NULL; ++ ++ kdbus_printf("STARTING TEST 'metadata-ns'.\n"); ++ ++ monitor = kdbus_hello(bus, KDBUS_HELLO_MONITOR, NULL, 0); ++ ASSERT_EXIT(monitor); ++ ++ /* ++ * parent will signal to child that is in its ++ * userns to read its queue ++ */ ++ efd = eventfd(0, EFD_CLOEXEC); ++ ASSERT_RETURN_VAL(efd >= 0, efd); ++ ++ ppid = getppid(); ++ ++ 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, ppid, efd); ++ _exit(ret); ++ } ++ ++ ++ /* Phase 1) privileged receives from unprivileged */ ++ ++ /* ++ * Receive from the unprivileged child ++ */ ++ kdbus_printf("\nUnprivileged → privileged (same namespaces):\n"); ++ ret = kdbus_msg_recv_poll(conn, 300, &msg, NULL); ++ ASSERT_RETURN(ret == 0); ++ ++ unpriv_conn_id = msg->src_id; ++ ++ /* Unprivileged user */ ++ ret = kdbus_match_kdbus_creds(msg, &unmapped_creds); ++ ASSERT_RETURN(ret == 0); ++ ++ /* Set the expected creds_pids */ ++ expected_pids = (struct kdbus_pids) { ++ .pid = pid, ++ .tid = pid, ++ .ppid = getpid(), ++ }; ++ ret = kdbus_match_kdbus_pids(msg, &expected_pids); ++ ASSERT_RETURN(ret == 0); ++ ++ kdbus_msg_free(msg); ++ ++ ++ /* ++ * Receive from the unprivileged that is in his own ++ * userns and pidns ++ */ ++ ++ kdbus_printf("\nUnprivileged/privileged in its userns → privileged " ++ "(different userns and pidns)\n"); ++ ret = kdbus_msg_recv_poll(conn, 300, &msg, NULL); ++ if (ret == -ETIMEDOUT) ++ /* perhaps unprivileged userns is not allowed */ ++ goto wait; ++ ++ ASSERT_RETURN(ret == 0); ++ ++ userns_conn_id = msg->src_id; ++ ++ /* We do not share the userns, os no KDBUS_ITEM_CAPS */ ++ item = kdbus_get_item(msg, KDBUS_ITEM_CAPS); ++ ASSERT_RETURN(item == NULL); ++ ++ /* ++ * Compare received items, creds must be translated into ++ * the receiver user namespace, so the user is unprivileged ++ */ ++ ret = kdbus_match_kdbus_creds(msg, &unmapped_creds); ++ ASSERT_RETURN(ret == 0); ++ ++ /* ++ * We should have the kdbus_pids since we are the parent ++ * pidns ++ */ ++ item = kdbus_get_item(msg, KDBUS_ITEM_PIDS); ++ ASSERT_RETURN(item); ++ ++ ASSERT_RETURN(memcmp(&item->pids, &unmapped_pids, ++ sizeof(struct kdbus_pids)) != 0); ++ ++ /* ++ * Parent pid of the unprivileged/privileged in its userns ++ * is the unprivileged child pid that was forked here. ++ */ ++ ASSERT_RETURN((uint64_t)pid == item->pids.ppid); ++ ++ kdbus_msg_free(msg); ++ ++ ++ /* Phase 2) Privileged connection sends now 3 packets */ ++ ++ /* ++ * Sending to unprivileged connections a unicast ++ */ ++ ret = kdbus_msg_send(conn, NULL, 0xdeadbeef, 0, 0, ++ 0, unpriv_conn_id); ++ ASSERT_RETURN(ret == 0); ++ ++ /* signal to child that is in its userns */ ++ ret = eventfd_write(efd, 1); ++ ASSERT_EXIT(ret == 0); ++ ++ /* ++ * Sending to unprivileged/privilged in its userns ++ * connections a unicast ++ */ ++ ret = kdbus_msg_send(conn, NULL, 0xdeadbeef, 0, 0, ++ 0, userns_conn_id); ++ ASSERT_RETURN(ret == 0); ++ ++ /* ++ * Sending to unprivileged connections a broadcast ++ */ ++ ret = kdbus_msg_send(conn, NULL, 0xdeadbeef, 0, 0, ++ 0, KDBUS_DST_ID_BROADCAST); ++ ASSERT_RETURN(ret == 0); ++ ++ ++wait: ++ ret = waitpid(pid, &status, 0); ++ ASSERT_RETURN(ret >= 0); ++ ++ ASSERT_RETURN(WIFEXITED(status)) ++ ASSERT_RETURN(!WEXITSTATUS(status)); ++ ++ /* Dump monitor queue */ ++ kdbus_printf("\n\nMonitor queue:\n"); ++ for (;;) { ++ ret = kdbus_msg_recv_poll(monitor, 100, &msg, NULL); ++ if (ret < 0) ++ break; ++ ++ if (msg->payload_type == KDBUS_PAYLOAD_DBUS) { ++ /* ++ * Parent pidns should see all the ++ * pids ++ */ ++ item = kdbus_get_item(msg, KDBUS_ITEM_PIDS); ++ ASSERT_RETURN(item); ++ ++ ASSERT_RETURN(item->pids.pid != 0 && ++ item->pids.tid != 0 && ++ item->pids.ppid != 0); ++ } ++ ++ kdbus_msg_free(msg); ++ } ++ ++ kdbus_conn_free(monitor); ++ close(efd); ++ ++ return 0; ++} ++ ++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 and all uids/gids ++ * should be mapped (we can just require the necessary ones) ++ */ ++ if (!config_user_ns_is_enabled() || ++ !all_uids_gids_are_mapped()) ++ 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; ++} +diff --git a/tools/testing/selftests/kdbus/test-monitor.c b/tools/testing/selftests/kdbus/test-monitor.c +new file mode 100644 +index 000000000000..e00d738a3986 +--- /dev/null ++++ b/tools/testing/selftests/kdbus/test-monitor.c +@@ -0,0 +1,176 @@ ++#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/mman.h> ++#include <sys/capability.h> ++#include <sys/wait.h> ++ ++#include "kdbus-util.h" ++#include "kdbus-enum.h" ++ ++#include "kdbus-util.h" ++#include "kdbus-enum.h" ++#include "kdbus-test.h" ++ ++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; ++ ++ conn = kdbus_hello(env->buspath, 0, NULL, 0); ++ ASSERT_RETURN(conn); ++ ++ /* add matches to make sure the monitor do not trigger an item add or ++ * remove on connect and disconnect, respectively. ++ */ ++ ret = kdbus_add_match_id(conn, 0x1, KDBUS_ITEM_ID_ADD, ++ KDBUS_MATCH_ID_ANY); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = kdbus_add_match_id(conn, 0x2, KDBUS_ITEM_ID_REMOVE, ++ KDBUS_MATCH_ID_ANY); ++ ASSERT_RETURN(ret == 0); ++ ++ /* register a monitor */ ++ monitor = kdbus_hello(env->buspath, KDBUS_HELLO_MONITOR, NULL, 0); ++ ASSERT_RETURN(monitor); ++ ++ /* make sure we did not receive a monitor connect notification */ ++ ret = kdbus_msg_recv(conn, &msg, &offset); ++ ASSERT_RETURN(ret == -EAGAIN); ++ ++ /* check that a monitor cannot acquire a name */ ++ ret = kdbus_name_acquire(monitor, "foo.bar.baz", NULL); ++ ASSERT_RETURN(ret == -EOPNOTSUPP); ++ ++ ret = kdbus_msg_send(env->conn, NULL, cookie, 0, 0, 0, conn->id); ++ ASSERT_RETURN(ret == 0); ++ ++ /* the recipient should have gotten 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); ++ ++ /* Installing matches for monitors must fais must fail */ ++ ret = kdbus_add_match_empty(monitor); ++ ASSERT_RETURN(ret == -EOPNOTSUPP); ++ ++ 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. */ ++ 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 recv ++ */ ++ ++ ret = kdbus_conn_update_attach_flags(monitor, ++ _KDBUS_ATTACH_ALL, ++ 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_ALL, ++ 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); ++ /* make sure we did not receive a monitor disconnect notification */ ++ ret = kdbus_msg_recv(conn, &msg, &offset); ++ ASSERT_RETURN(ret == -EAGAIN); ++ ++ kdbus_conn_free(conn); ++ ++ /* Make sure that monitor as unprivileged is not allowed */ ++ ret = test_is_capable(CAP_SETUID, CAP_SETGID, -1); ++ ASSERT_RETURN(ret >= 0); ++ ++ if (ret && all_uids_gids_are_mapped()) { ++ ret = RUN_UNPRIVILEGED(UNPRIV_UID, UNPRIV_UID, ({ ++ monitor = kdbus_hello(env->buspath, ++ KDBUS_HELLO_MONITOR, ++ NULL, 0); ++ ASSERT_EXIT(!monitor && errno == EPERM); ++ ++ _exit(EXIT_SUCCESS); ++ }), ++ ({ 0; })); ++ ASSERT_RETURN(ret == 0); ++ } ++ ++ return TEST_OK; ++} +diff --git a/tools/testing/selftests/kdbus/test-names.c b/tools/testing/selftests/kdbus/test-names.c +new file mode 100644 +index 000000000000..66ebb47370eb +--- /dev/null ++++ b/tools/testing/selftests/kdbus/test-names.c +@@ -0,0 +1,194 @@ ++#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 <getopt.h> ++#include <stdbool.h> ++ ++#include "kdbus-api.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_list cmd_list = { .size = sizeof(cmd_list) }; ++ struct kdbus_info *name, *list; ++ bool found = false; ++ int ret; ++ ++ cmd_list.flags = KDBUS_LIST_NAMES; ++ ++ ret = kdbus_cmd_list(conn->fd, &cmd_list); ++ ASSERT_RETURN(ret == 0); ++ ++ list = (struct kdbus_info *)(conn->buf + cmd_list.offset); ++ KDBUS_FOREACH(name, list, cmd_list.list_size) { ++ struct kdbus_item *item; ++ const char *n = NULL; ++ ++ KDBUS_ITEM_FOREACH(item, name, items) ++ if (item->type == KDBUS_ITEM_OWNED_NAME) ++ n = item->name.name; ++ ++ if (name->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) ++{ ++ struct kdbus_conn *conn; ++ 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.*"; ++ ++ /* create a 2nd connection */ ++ conn = kdbus_hello(env->buspath, 0, NULL, 0); ++ ASSERT_RETURN(conn != NULL); ++ ++ /* acquire name "foo.bar.xxx" name */ ++ ret = kdbus_name_acquire(conn, "foo.bar.xxx", NULL); ++ ASSERT_RETURN(ret == 0); ++ ++ /* 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 == -EADDRINUSE); ++ ++ /* Name is not valid, must fail */ ++ ret = kdbus_name_release(env->conn, dot_name); ++ ASSERT_RETURN(ret == -ESRCH); ++ ++ ret = kdbus_name_release(env->conn, invalid_name); ++ ASSERT_RETURN(ret == -ESRCH); ++ ++ ret = kdbus_name_release(env->conn, wildcard_name); ++ ASSERT_RETURN(ret == -ESRCH); ++ ++ kdbus_conn_free(conn); ++ ++ 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; ++} +diff --git a/tools/testing/selftests/kdbus/test-policy-ns.c b/tools/testing/selftests/kdbus/test-policy-ns.c +new file mode 100644 +index 000000000000..3437012f90af +--- /dev/null ++++ b/tools/testing/selftests/kdbus/test-policy-ns.c +@@ -0,0 +1,632 @@ ++/* ++ * Test metadata and policies in new namespaces. Even if our tests ++ * can run in a namespaced setup, this test is necessary so we can ++ * inspect policies on the same kdbusfs but between multiple ++ * namespaces. ++ * ++ * Copyright (C) 2014-2015 Djalal Harouni ++ * ++ * kdbus is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU Lesser General Public License as published by the ++ * Free Software Foundation; either version 2.1 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#include <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/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" ++ ++#define MAX_CONN 64 ++#define POLICY_NAME "foo.test.policy-test" ++ ++#define KDBUS_CONN_MAX_MSGS_PER_USER 16 ++ ++/** ++ * Note: this test can be used to inspect policy_db->talk_access_hash ++ * ++ * 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, 200, 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; ++ bool pool_full = false; ++ unsigned int sent_packets = 0; ++ unsigned int lost_packets = 0; ++ 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 (%m)\n", ++ ret); ++ 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) { ++ /* ++ * Receivers are not reading their messages, ++ * not scheduled ?! ++ * ++ * So set the pool full here, perhaps the ++ * connection pool or queue was full, later ++ * recheck receivers errors ++ */ ++ if (ret == -ENOBUFS || ret == -EXFULL) ++ pool_full = true; ++ break; ++ } ++ ++ sent_packets++; ++ } ++ ++ 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) { ++ /* Update only if send did not fail */ ++ if (ret == 0) ++ ret = thread_ret; ++ ++ lost_packets++; ++ } ++ } ++ } ++ ++ /* ++ * When sending if we did fail with -ENOBUFS or -EXFULL ++ * then we should have set lost_packet and we should at ++ * least have sent_packets set to KDBUS_CONN_MAX_MSGS_PER_USER ++ */ ++ if (pool_full) { ++ ASSERT_RETURN(lost_packets > 0); ++ ++ /* ++ * We should at least send KDBUS_CONN_MAX_MSGS_PER_USER ++ * ++ * For every send operation we create a thread to ++ * recv the packet, so we keep the queue clean ++ */ ++ ASSERT_RETURN(sent_packets >= KDBUS_CONN_MAX_MSGS_PER_USER); ++ ++ /* ++ * Set ret to zero since we only failed due to ++ * the receiving threads that have not been ++ * scheduled ++ */ ++ ret = 0; ++ } ++ ++ 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 children 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; ++ ++ 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; ++ ++ 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; ++ ++ 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; ++ ++ ret = test_is_capable(CAP_SETUID, CAP_SETGID, -1); ++ ASSERT_RETURN(ret >= 0); ++ ++ /* no enough privileges, SKIP test */ ++ if (!ret) ++ return TEST_SKIP; ++ ++ /* we require user-namespaces */ ++ if (access("/proc/self/uid_map", F_OK) != 0) ++ return TEST_SKIP; ++ ++ /* uids/gids must be mapped */ ++ if (!all_uids_gids_are_mapped()) ++ 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_list(conn_db[0], KDBUS_LIST_NAMES | ++ KDBUS_LIST_UNIQUE | ++ KDBUS_LIST_ACTIVATORS | ++ KDBUS_LIST_QUEUED); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = kdbus_fork_test(bus, POLICY_NAME, conn_db, EXIT_SUCCESS); ++ ASSERT_RETURN(ret == 0); ++ ++ /* ++ * children 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 children 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; ++} +diff --git a/tools/testing/selftests/kdbus/test-policy-priv.c b/tools/testing/selftests/kdbus/test-policy-priv.c +new file mode 100644 +index 000000000000..a318cccad0d5 +--- /dev/null ++++ b/tools/testing/selftests/kdbus/test-policy-priv.c +@@ -0,0 +1,1269 @@ ++#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/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 = 0; ++ 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, 300, 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 efd; ++ int ret = 0; ++ 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); ++ ++ /* Use a little bit high time */ ++ ret = kdbus_msg_recv_poll(child, 500, &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); ++ ++ /* Use a little bit high time */ ++ ret = kdbus_msg_recv_poll(child_2, 1000, ++ &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_EXIT(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); ++ ++ /* Use a little bit high time */ ++ ret = kdbus_msg_recv_poll(child_2, 1000, ++ &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; })); ++ ASSERT_RETURN(ret == 0); ++ } ++ })); ++ ASSERT_RETURN(ret == 0); ++ ++ close(efd); ++ ++ return ret; ++} ++ ++static void nosig(int sig) ++{ ++} ++ ++static int test_priv_before_policy_upload(struct kdbus_test_env *env) ++{ ++ int ret = 0; ++ 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_EXPECT_REPLY will fail with -ENOTUNIQ ++ */ ++ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ ++ ret = kdbus_msg_send(unpriv, NULL, 0xdeadbeef, ++ KDBUS_MSG_EXPECT_REPLY, ++ 5000000000ULL, 0, ++ KDBUS_DST_ID_BROADCAST); ++ ASSERT_EXIT(ret == -ENOTUNIQ); ++ })); ++ ASSERT_RETURN(ret == 0); ++ ++ /* ++ * Test broadcast with a privileged connection. ++ * ++ * The first unprivileged receiver should not get the ++ * broadcast message sent by the privileged connection, ++ * since there is no a TALK policy that allows the ++ * unprivileged to TALK to the privileged connection. It ++ * will fail with -ETIMEDOUT ++ * ++ * Then second case: ++ * The privileged connection should get the broadcast ++ * message from the unprivileged one. Since the receiver is ++ * a privileged bus user and it has default TALK access to ++ * all connections it will receive those. ++ */ ++ ++ ret = test_policy_priv_by_broadcast(env->buspath, conn, ++ DO_NOT_DROP, ++ 0, -ETIMEDOUT); ++ 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, 0, 0); ++ 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. ++ */ ++ ++ ++expected_cookie; ++ ret = test_policy_priv_by_id(env->buspath, owner_a, false, ++ -ETIMEDOUT, -EPERM); ++ ASSERT_RETURN(ret == 0); ++ ++ /* ++ * Make sure that privileged won't receive broadcasts unless ++ * it installs a match. It will fail with -ETIMEDOUT ++ * ++ * At same time check that the unprivileged connection will ++ * not receive the broadcast message from the privileged one ++ * since the privileged one owns a name with a restricted ++ * policy TALK (actually the TALK policy is still not ++ * registered so we fail by default), thus the unprivileged ++ * receiver is not able to TALK to that name. ++ */ ++ ++ ret = test_policy_priv_by_broadcast(env->buspath, owner_a, ++ DO_NOT_DROP, ++ -ETIMEDOUT, -ETIMEDOUT); ++ 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 owner_a is ++ * able to TALK to any connection so it will receive the ++ * broadcast message now. ++ */ ++ ++ ret = test_policy_priv_by_broadcast(env->buspath, owner_a, ++ DO_NOT_DROP, ++ 0, -ETIMEDOUT); ++ 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, 0, 0); ++ 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); ++ ++ 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); ++ ++ /* Free connections and their received messages and restart */ ++ kdbus_conn_free(owner_a); ++ ++ owner_a = kdbus_hello(env->buspath, 0, NULL, 0); ++ ASSERT_RETURN(owner_a); ++ ++ /* Activate matching for a privileged connection */ ++ ret = kdbus_add_match_empty(owner_a); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = kdbus_name_acquire(owner_a, "com.example.broadcastA", NULL); ++ ASSERT_EXIT(ret >= 0); ++ ++ 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 have a TALK access by default ++ * they are able to signal each other using broadcast due to ++ * the fact they are privileged connections, they receive ++ * all broadcasts if the match allows it. ++ */ ++ ++ ++expected_cookie; ++ ret = kdbus_msg_send(owner_a, NULL, expected_cookie, 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); ++ ASSERT_RETURN(msg->cookie == expected_cookie); ++ ++ /* Check src ID */ ++ ASSERT_RETURN(msg->src_id == owner_a->id); ++ ++ kdbus_msg_free(msg); ++ ++ /* 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); ++ ++ /* ++ * Send broadcasts from an unprivileged connection that ++ * owns a name "com.example.broadcastB". ++ * ++ * We'll have four destinations here: ++ * ++ * 1) destination owner_a: privileged connection that owns ++ * "com.example.broadcastA". It will receive the broadcast ++ * since it is a privileged has default TALK access to all ++ * connections, and it is subscribed to the match. ++ * Will succeed. ++ * ++ * 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 since it has default ++ * TALK access to all connection. ++ * ++ * 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, 300, &msg, NULL); ++ ASSERT_EXIT(ret == 0 && msg->cookie == expected_cookie); ++ ++ /* ++ * Did not install matches, not interested in ++ * broadcasts ++ */ ++ ret = kdbus_msg_recv_poll(unpriv_b, 300, 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, 300, &msg, NULL); ++ ASSERT_RETURN(ret == 0); ++ ++ /* confirm the received cookie */ ++ ASSERT_RETURN(msg->cookie == expected_cookie); ++ ++ kdbus_msg_free(msg); ++ ++ /* ++ * owner_b got the broadcast from an unprivileged ++ * connection. ++ */ ++ ret = kdbus_msg_recv_poll(owner_b, 300, &msg, NULL); ++ ASSERT_RETURN(ret == 0); ++ ++ /* confirm the received cookie */ ++ ASSERT_RETURN(msg->cookie == expected_cookie); ++ ++ kdbus_msg_free(msg); ++ ++ })); ++ 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); ++ ++ /* Drop received broadcasts by privileged */ ++ ret = kdbus_msg_recv_poll(owner_a, 100, NULL, NULL); ++ ret = kdbus_msg_recv_poll(owner_a, 100, NULL, NULL); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = kdbus_msg_recv(owner_a, NULL, NULL); ++ ASSERT_RETURN(ret == -EAGAIN); ++ ++ ret = kdbus_msg_recv_poll(owner_b, 100, NULL, NULL); ++ ret = kdbus_msg_recv_poll(owner_b, 100, NULL, NULL); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = kdbus_msg_recv(owner_b, NULL, NULL); ++ ASSERT_RETURN(ret == -EAGAIN); ++ ++ /* ++ * Perform last tests, allow others to talk to name ++ * "com.example.broadcastA". So now receiving broadcasts ++ * from it should succeed since the TALK 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); ++ ++ /* ++ * Unprivileged is able to TALK to "com.example.broadcastA" ++ * now so it will receive its broadcasts ++ */ ++ ret = test_policy_priv_by_broadcast(env->buspath, owner_a, ++ DO_NOT_DROP, 0, 0); ++ 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 is privileged it will get the broadcast now. */ ++ ret = kdbus_msg_recv_poll(owner_a, 300, &msg, NULL); ++ ASSERT_RETURN(ret == 0); ++ ++ /* confirm the received cookie */ ++ ASSERT_RETURN(msg->cookie == expected_cookie); ++ ++ kdbus_msg_free(msg); ++ ++ /* ++ * owner_a released name "com.example.broadcastA". It should ++ * receive broadcasts since it is still privileged and has ++ * the right match. ++ * ++ * Unprivileged connection will own a name and will try to ++ * signal to the privileged connection. ++ */ ++ ++ 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, 300, &msg, NULL); ++ ASSERT_RETURN(ret == 0); ++ ++ /* confirm the received cookie */ ++ ASSERT_RETURN(msg->cookie == expected_cookie); ++ ++ kdbus_msg_free(msg); ++ ++ 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; ++ ++ /* make sure that uids and gids are mapped */ ++ if (!all_uids_gids_are_mapped()) ++ 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); ++ ++ /* ++ * Make sure unprivileged are not able to register policy ++ * holders ++ */ ++ ++ ret = RUN_UNPRIVILEGED(UNPRIV_UID, UNPRIV_GID, ({ ++ struct kdbus_conn *holder; ++ ++ holder = kdbus_hello_registrar(env->buspath, ++ "com.example.a", NULL, 0, ++ KDBUS_HELLO_POLICY_HOLDER); ++ ASSERT_EXIT(holder == NULL && errno == EPERM); ++ }), ++ ({ 0; })); ++ 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, ++ }; ++ ++ /* ++ * Make sure unprivileged/normal connections are not able ++ * to update policies ++ */ ++ ++ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ ++ ret = kdbus_conn_update_policy(unpriv, "com.example.a", ++ &access, 1); ++ ASSERT_EXIT(ret == -EOPNOTSUPP); ++ })); ++ ASSERT_RETURN(ret == 0); ++ ++ 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, 300, 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; ++} +diff --git a/tools/testing/selftests/kdbus/test-policy.c b/tools/testing/selftests/kdbus/test-policy.c +new file mode 100644 +index 000000000000..96d20d5e9172 +--- /dev/null ++++ b/tools/testing/selftests/kdbus/test-policy.c +@@ -0,0 +1,80 @@ ++#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 "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; ++} +diff --git a/tools/testing/selftests/kdbus/test-sync.c b/tools/testing/selftests/kdbus/test-sync.c +new file mode 100644 +index 000000000000..e2be910d2ece +--- /dev/null ++++ b/tools/testing/selftests/kdbus/test-sync.c +@@ -0,0 +1,369 @@ ++#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 <pthread.h> ++#include <stdbool.h> ++#include <signal.h> ++#include <sys/wait.h> ++#include <sys/eventfd.h> ++ ++#include "kdbus-api.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 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_EXIT(ret == 0); ++ ++ ret = kdbus_msg_send_sync(conn_dst, NULL, cookie, ++ KDBUS_MSG_EXPECT_REPLY, ++ 100000000ULL, 0, conn_src->id, -1); ++ 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 int close_epipe_sync(const char *bus) ++{ ++ pid_t pid; ++ int ret, status; ++ struct kdbus_conn *conn_src; ++ struct kdbus_conn *conn_dst; ++ struct kdbus_msg *msg = NULL; ++ ++ conn_src = kdbus_hello(bus, 0, NULL, 0); ++ ASSERT_RETURN(conn_src); ++ ++ ret = kdbus_add_match_empty(conn_src); ++ ASSERT_RETURN(ret == 0); ++ ++ conn_dst = kdbus_hello(bus, 0, NULL, 0); ++ ASSERT_RETURN(conn_dst); ++ ++ cookie++; ++ pid = fork(); ++ ASSERT_RETURN_VAL(pid >= 0, pid); ++ ++ if (pid == 0) { ++ uint64_t dst_id; ++ ++ /* close our reference */ ++ dst_id = conn_dst->id; ++ kdbus_conn_free(conn_dst); ++ ++ ret = kdbus_msg_recv_poll(conn_src, 100, &msg, NULL); ++ ASSERT_EXIT(ret == 0 && msg->cookie == cookie); ++ ASSERT_EXIT(msg->src_id == dst_id); ++ ++ cookie++; ++ ret = kdbus_msg_send_sync(conn_src, NULL, cookie, ++ KDBUS_MSG_EXPECT_REPLY, ++ 100000000ULL, 0, dst_id, -1); ++ ASSERT_EXIT(ret == -EPIPE); ++ ++ _exit(EXIT_SUCCESS); ++ } ++ ++ ret = kdbus_msg_send(conn_dst, NULL, cookie, 0, 0, 0, ++ KDBUS_DST_ID_BROADCAST); ++ ASSERT_RETURN(ret == 0); ++ ++ cookie++; ++ ret = kdbus_msg_recv_poll(conn_dst, 100, &msg, NULL); ++ ASSERT_RETURN(ret == 0 && msg->cookie == cookie); ++ ++ kdbus_msg_free(msg); ++ ++ /* destroy connection */ ++ kdbus_conn_free(conn_dst); ++ kdbus_conn_free(conn_src); ++ ++ ret = waitpid(pid, &status, 0); ++ ASSERT_RETURN_VAL(ret >= 0, ret); ++ ++ if (!WIFEXITED(status)) ++ return TEST_ERR; ++ ++ return (status == EXIT_SUCCESS) ? TEST_OK : TEST_ERR; ++} ++ ++static int cancel_fd_sync(struct kdbus_conn *conn_src, ++ struct kdbus_conn *conn_dst) ++{ ++ pid_t pid; ++ int cancel_fd; ++ int ret, status; ++ uint64_t counter = 1; ++ struct kdbus_msg *msg = NULL; ++ ++ cancel_fd = eventfd(0, 0); ++ ASSERT_RETURN_VAL(cancel_fd >= 0, cancel_fd); ++ ++ cookie++; ++ pid = fork(); ++ ASSERT_RETURN_VAL(pid >= 0, pid); ++ ++ if (pid == 0) { ++ ret = kdbus_msg_send_sync(conn_dst, NULL, cookie, ++ KDBUS_MSG_EXPECT_REPLY, ++ 100000000ULL, 0, conn_src->id, ++ cancel_fd); ++ ASSERT_EXIT(ret == -ECANCELED); ++ ++ _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 = write(cancel_fd, &counter, sizeof(counter)); ++ ASSERT_RETURN(ret == sizeof(counter)); ++ ++ ret = waitpid(pid, &status, 0); ++ ASSERT_RETURN_VAL(ret >= 0, ret); ++ ++ if (WIFSIGNALED(status)) ++ return TEST_ERR; ++ ++ return (status == EXIT_SUCCESS) ? TEST_OK : TEST_ERR; ++} ++ ++static int no_cancel_sync(struct kdbus_conn *conn_src, ++ struct kdbus_conn *conn_dst) ++{ ++ pid_t pid; ++ int cancel_fd; ++ int ret, status; ++ struct kdbus_msg *msg = NULL; ++ ++ /* pass eventfd, but never signal it so it shouldn't have any effect */ ++ ++ cancel_fd = eventfd(0, 0); ++ ASSERT_RETURN_VAL(cancel_fd >= 0, cancel_fd); ++ ++ cookie++; ++ pid = fork(); ++ ASSERT_RETURN_VAL(pid >= 0, pid); ++ ++ if (pid == 0) { ++ ret = kdbus_msg_send_sync(conn_dst, NULL, cookie, ++ KDBUS_MSG_EXPECT_REPLY, ++ 100000000ULL, 0, conn_src->id, ++ cancel_fd); ++ ASSERT_EXIT(ret == 0); ++ ++ _exit(EXIT_SUCCESS); ++ } ++ ++ ret = kdbus_msg_recv_poll(conn_src, 100, &msg, NULL); ++ ASSERT_RETURN_VAL(ret == 0 && msg->cookie == cookie, -1); ++ ++ kdbus_msg_free(msg); ++ ++ ret = kdbus_msg_send_reply(conn_src, cookie, conn_dst->id); ++ ASSERT_RETURN_VAL(ret >= 0, ret); ++ ++ ret = waitpid(pid, &status, 0); ++ ASSERT_RETURN_VAL(ret >= 0, ret); ++ ++ if (WIFSIGNALED(status)) ++ return -1; ++ ++ return (status == EXIT_SUCCESS) ? 0 : -1; ++} ++ ++static void *run_thread_reply(void *data) ++{ ++ int ret; ++ unsigned long status = TEST_OK; ++ ++ ret = kdbus_msg_recv_poll(conn_a, 3000, NULL, NULL); ++ if (ret < 0) ++ goto exit_thread; ++ ++ kdbus_printf("Thread received message, sending reply ...\n"); ++ ++ /* using an unknown cookie must fail */ ++ ret = kdbus_msg_send_reply(conn_a, ~cookie, conn_b->id); ++ if (ret != -EPERM) { ++ status = TEST_ERR; ++ goto exit_thread; ++ } ++ ++ ret = kdbus_msg_send_reply(conn_a, cookie, conn_b->id); ++ if (ret != 0) { ++ status = TEST_ERR; ++ goto exit_thread; ++ } ++ ++exit_thread: ++ pthread_exit(NULL); ++ return (void *) status; ++} ++ ++int kdbus_test_sync_reply(struct kdbus_test_env *env) ++{ ++ unsigned long status; ++ 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_sync(conn_b, NULL, cookie, ++ KDBUS_MSG_EXPECT_REPLY, ++ 5000000000ULL, 0, conn_a->id, -1); ++ ++ pthread_join(thread, (void *) &status); ++ ASSERT_RETURN(status == 0); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = interrupt_sync(conn_a, conn_b); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = close_epipe_sync(env->buspath); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = cancel_fd_sync(conn_a, conn_b); ++ ASSERT_RETURN(ret == 0); ++ ++ ret = no_cancel_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) ++{ ++ struct kdbus_cmd cmd_byebye = { .size = sizeof(cmd_byebye) }; ++ 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) ++ kdbus_cmd_byebye(conn_b->fd, &cmd_byebye); ++ else if (data == BYEBYE_THEM) ++ kdbus_cmd_byebye(conn_a->fd, &cmd_byebye); ++ } ++ ++ 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_sync(conn_b, NULL, cookie, ++ KDBUS_MSG_EXPECT_REPLY, ++ 5000000000ULL, 0, conn_a->id, -1); ++ ++ 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_sync(conn_b, NULL, cookie, ++ KDBUS_MSG_EXPECT_REPLY, ++ 5000000000ULL, 0, conn_a->id, -1); ++ ++ ASSERT_RETURN(ret == -EPIPE); ++ ++ pthread_join(thread, NULL); ++ ++ kdbus_conn_free(conn_a); ++ kdbus_conn_free(conn_b); ++ ++ return TEST_OK; ++} +diff --git a/tools/testing/selftests/kdbus/test-timeout.c b/tools/testing/selftests/kdbus/test-timeout.c +new file mode 100644 +index 000000000000..cfd193066a64 +--- /dev/null ++++ b/tools/testing/selftests/kdbus/test-timeout.c +@@ -0,0 +1,99 @@ ++#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 <stdbool.h> ++ ++#include "kdbus-api.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 = { .size = sizeof(recv) }; ++ struct kdbus_msg *msg; ++ int ret; ++ ++ ret = kdbus_cmd_recv(conn->fd, &recv); ++ if (ret < 0) { ++ kdbus_printf("error receiving message: %d (%m)\n", ret); ++ return ret; ++ } ++ ++ msg = (struct kdbus_msg *)(conn->buf + recv.msg.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.msg.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; ++ uint64_t cookie = 0xdeadbeef; ++ ++ 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++, cookie++) { ++ kdbus_printf("Sending message with cookie %llu ...\n", ++ (unsigned long long)cookie); ++ ASSERT_RETURN(kdbus_msg_send(conn_b, NULL, cookie, ++ KDBUS_MSG_EXPECT_REPLY, ++ (i + 1) * 100ULL * 1000000ULL, 0, ++ conn_a->id) == 0); ++ expected |= 1ULL << cookie; ++ } ++ ++ 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/kdbus-add-uapi-header-file.patch b/kdbus-add-uapi-header-file.patch new file mode 100644 index 000000000..494bd3a94 --- /dev/null +++ b/kdbus-add-uapi-header-file.patch @@ -0,0 +1,1040 @@ +From: Daniel Mack <daniel@zonque.org> +Date: Thu, 11 Sep 2014 18:38:06 +0200 +Subject: [PATCH] kdbus: add uapi header file + +This patch adds the header file which describes the low-level +transport protocol used by various ioctls. The header file is located +in include/uapi/linux/ as it is shared between kernel and userspace, +and it only contains data structure definitions, enums and defines +for constants. + +The low-level kernel API of kdbus is exposed through ioctls, employed +on nodes exposed by kdbusfs. We've chosen a ioctl-based implementation +over syscalls for various reaons: + + * The ioctls kdbus offers are completely specific to nodes exposed by + kdbusfs and can not be applied to any other file descriptor in a + system. + + * The file descriptors derived from opening nodes in kdbusfs can only be + used for poll(), close() and the ioctls described in kdbus.h. + + * Not all systems will make use of kdbus eventually, and we want to + make as many parts of the kernel optional at build time. + + * We want to build the kdbus code as module, which is impossible to + do when implemented with syscalls. + + * The ioctl dispatching logic does not show up in our performance + graphs; its overhead is negligible. + + * For development, being able to build, load and unload a separate + module with a versioned name suffix is essential. + +Signed-off-by: Daniel Mack <daniel@zonque.org> +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Djalal Harouni <tixxdz@opendz.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + include/uapi/linux/Kbuild | 1 + + include/uapi/linux/kdbus.h | 979 +++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 980 insertions(+) + create mode 100644 include/uapi/linux/kdbus.h + +diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild +index 1ff9942718fe..67a4c60e1deb 100644 +--- a/include/uapi/linux/Kbuild ++++ b/include/uapi/linux/Kbuild +@@ -216,6 +216,7 @@ header-y += ixjuser.h + header-y += jffs2.h + header-y += joystick.h + header-y += kcmp.h ++header-y += kdbus.h + header-y += kdev_t.h + header-y += kd.h + header-y += kernelcapi.h +diff --git a/include/uapi/linux/kdbus.h b/include/uapi/linux/kdbus.h +new file mode 100644 +index 000000000000..fc1d77dd7c93 +--- /dev/null ++++ b/include/uapi/linux/kdbus.h +@@ -0,0 +1,979 @@ ++/* ++ * 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_NEGOTIATE (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; ++} __attribute__((__aligned__(8))); ++ ++/** ++ * 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]; ++} __attribute__((__aligned__(8))); ++ ++/** ++ * struct kdbus_creds - process credentials ++ * @uid: User ID ++ * @euid: Effective UID ++ * @suid: Saved UID ++ * @fsuid: Filesystem UID ++ * @gid: Group ID ++ * @egid: Effective GID ++ * @sgid: Saved GID ++ * @fsgid: Filesystem GID ++ * ++ * Attached to: ++ * KDBUS_ITEM_CREDS ++ */ ++struct kdbus_creds { ++ __u64 uid; ++ __u64 euid; ++ __u64 suid; ++ __u64 fsuid; ++ __u64 gid; ++ __u64 egid; ++ __u64 sgid; ++ __u64 fsgid; ++} __attribute__((__aligned__(8))); ++ ++/** ++ * struct kdbus_pids - process identifiers ++ * @pid: Process ID ++ * @tid: Thread ID ++ * @ppid: Parent process ID ++ * ++ * The PID and TID of a process. ++ * ++ * Attached to: ++ * KDBUS_ITEM_PIDS ++ */ ++struct kdbus_pids { ++ __u64 pid; ++ __u64 tid; ++ __u64 ppid; ++} __attribute__((__aligned__(8))); ++ ++/** ++ * 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]; ++} __attribute__((__aligned__(8))); ++ ++/** ++ * struct kdbus_audit - audit information ++ * @sessionid: The audit session ID ++ * @loginuid: The audit login uid ++ * ++ * Attached to: ++ * KDBUS_ITEM_AUDIT ++ */ ++struct kdbus_audit { ++ __u32 sessionid; ++ __u32 loginuid; ++} __attribute__((__aligned__(8))); ++ ++/** ++ * 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; ++} __attribute__((__aligned__(8))); ++ ++/** ++ * 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; ++ }; ++} __attribute__((__aligned__(8))); ++ ++/** ++ * 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; ++} __attribute__((__aligned__(8))); ++ ++/** ++ * 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]; ++} __attribute__((__aligned__(8))); ++ ++/** ++ * struct kdbus_memfd - a kdbus memfd ++ * @start: The offset into the memfd where the segment starts ++ * @size: The size of the memfd segment ++ * @fd: The file descriptor number ++ * @__pad: Padding to ensure proper alignment and size ++ * ++ * Attached to: ++ * KDBUS_ITEM_PAYLOAD_MEMFD ++ */ ++struct kdbus_memfd { ++ __u64 start; ++ __u64 size; ++ int fd; ++ __u32 __pad; ++} __attribute__((__aligned__(8))); ++ ++/** ++ * struct kdbus_name - a registered well-known name with its flags ++ * @flags: Flags from KDBUS_NAME_* ++ * @name: Well-known name ++ * ++ * Attached to: ++ * KDBUS_ITEM_OWNED_NAME ++ */ ++struct kdbus_name { ++ __u64 flags; ++ char name[0]; ++} __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, ++}; ++ ++/** ++ * 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 */ ++} __attribute__((__aligned__(8))); ++ ++/** ++ * enum kdbus_attach_flags - flags for metadata attachments ++ * @KDBUS_ATTACH_TIMESTAMP: Timestamp ++ * @KDBUS_ATTACH_CREDS: Credentials ++ * @KDBUS_ATTACH_PIDS: PIDs ++ * @KDBUS_ATTACH_AUXGROUPS: Auxiliary groups ++ * @KDBUS_ATTACH_NAMES: Well-known names ++ * @KDBUS_ATTACH_TID_COMM: The "comm" process identifier of the TID ++ * @KDBUS_ATTACH_PID_COMM: 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_DESCRIPTION: The human-readable connection name ++ * @_KDBUS_ATTACH_ALL: All of the above ++ * @_KDBUS_ATTACH_ANY: Wildcard match to enable any kind of ++ * metatdata. ++ */ ++enum kdbus_attach_flags { ++ KDBUS_ATTACH_TIMESTAMP = 1ULL << 0, ++ KDBUS_ATTACH_CREDS = 1ULL << 1, ++ KDBUS_ATTACH_PIDS = 1ULL << 2, ++ KDBUS_ATTACH_AUXGROUPS = 1ULL << 3, ++ KDBUS_ATTACH_NAMES = 1ULL << 4, ++ KDBUS_ATTACH_TID_COMM = 1ULL << 5, ++ KDBUS_ATTACH_PID_COMM = 1ULL << 6, ++ KDBUS_ATTACH_EXE = 1ULL << 7, ++ KDBUS_ATTACH_CMDLINE = 1ULL << 8, ++ KDBUS_ATTACH_CGROUP = 1ULL << 9, ++ KDBUS_ATTACH_CAPS = 1ULL << 10, ++ KDBUS_ATTACH_SECLABEL = 1ULL << 11, ++ KDBUS_ATTACH_AUDIT = 1ULL << 12, ++ KDBUS_ATTACH_CONN_DESCRIPTION = 1ULL << 13, ++ _KDBUS_ATTACH_ALL = (1ULL << 14) - 1, ++ _KDBUS_ATTACH_ANY = ~0ULL ++}; ++ ++/** ++ * 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_NEGOTIATE: Negotiate supported 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_CANCEL_FD: FD used to cancel a synchronous ++ * operation by writing to it from ++ * userspace ++ * @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'sbloom filter ++ * @KDBUS_ITEM_DST_NAME: Destination's well-known name ++ * @KDBUS_ITEM_MAKE_NAME: Name of domain, bus, endpoint ++ * @KDBUS_ITEM_ATTACH_FLAGS_SEND: Attach-flags, used for updating which ++ * metadata a connection opts in to send ++ * @KDBUS_ITEM_ATTACH_FLAGS_RECV: Attach-flags, used for updating which ++ * metadata a connection requests to ++ * receive for each reeceived message ++ * @KDBUS_ITEM_ID: Connection ID ++ * @KDBUS_ITEM_NAME: Well-know name with flags ++ * @_KDBUS_ITEM_ATTACH_BASE: Start of metadata attach items ++ * @KDBUS_ITEM_TIMESTAMP: Timestamp ++ * @KDBUS_ITEM_CREDS: Process credentials ++ * @KDBUS_ITEM_PIDS: Process identifiers ++ * @KDBUS_ITEM_AUXGROUPS: Auxiliary process groups ++ * @KDBUS_ITEM_OWNED_NAME: A name owned by the associated ++ * connection ++ * @KDBUS_ITEM_TID_COMM: Thread ID "comm" identifier ++ * (Don't trust this, see below.) ++ * @KDBUS_ITEM_PID_COMM: Process ID "comm" identifier ++ * (Don't trust this, see below.) ++ * @KDBUS_ITEM_EXE: The path of the executable ++ * (Don't trust this, see below.) ++ * @KDBUS_ITEM_CMDLINE: The process command line ++ * (Don't trust this, see below.) ++ * @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_DESCRIPTION: 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: Notification in kdbus_notify_name_change ++ * @KDBUS_ITEM_NAME_REMOVE: Notification in kdbus_notify_name_change ++ * @KDBUS_ITEM_NAME_CHANGE: Notification in kdbus_notify_name_change ++ * @KDBUS_ITEM_ID_ADD: Notification in kdbus_notify_id_change ++ * @KDBUS_ITEM_ID_REMOVE: Notification in kdbus_notify_id_change ++ * @KDBUS_ITEM_REPLY_TIMEOUT: Timeout has been reached ++ * @KDBUS_ITEM_REPLY_DEAD: Destination died ++ * ++ * N.B: The process and thread COMM fields, as well as the CMDLINE and ++ * EXE fields may be altered by unprivileged processes und should ++ * hence *not* used for security decisions. Peers should make use of ++ * these items only for informational purposes, such as generating log ++ * records. ++ */ ++enum kdbus_item_type { ++ _KDBUS_ITEM_NULL, ++ _KDBUS_ITEM_USER_BASE, ++ KDBUS_ITEM_NEGOTIATE = _KDBUS_ITEM_USER_BASE, ++ KDBUS_ITEM_PAYLOAD_VEC, ++ KDBUS_ITEM_PAYLOAD_OFF, ++ KDBUS_ITEM_PAYLOAD_MEMFD, ++ KDBUS_ITEM_FDS, ++ KDBUS_ITEM_CANCEL_FD, ++ KDBUS_ITEM_BLOOM_PARAMETER, ++ KDBUS_ITEM_BLOOM_FILTER, ++ KDBUS_ITEM_BLOOM_MASK, ++ KDBUS_ITEM_DST_NAME, ++ KDBUS_ITEM_MAKE_NAME, ++ KDBUS_ITEM_ATTACH_FLAGS_SEND, ++ KDBUS_ITEM_ATTACH_FLAGS_RECV, ++ KDBUS_ITEM_ID, ++ KDBUS_ITEM_NAME, ++ ++ /* keep these item types in sync with KDBUS_ATTACH_* flags */ ++ _KDBUS_ITEM_ATTACH_BASE = 0x1000, ++ KDBUS_ITEM_TIMESTAMP = _KDBUS_ITEM_ATTACH_BASE, ++ KDBUS_ITEM_CREDS, ++ KDBUS_ITEM_PIDS, ++ KDBUS_ITEM_AUXGROUPS, ++ KDBUS_ITEM_OWNED_NAME, ++ KDBUS_ITEM_TID_COMM, ++ KDBUS_ITEM_PID_COMM, ++ KDBUS_ITEM_EXE, ++ KDBUS_ITEM_CMDLINE, ++ KDBUS_ITEM_CGROUP, ++ KDBUS_ITEM_CAPS, ++ KDBUS_ITEM_SECLABEL, ++ KDBUS_ITEM_AUDIT, ++ KDBUS_ITEM_CONN_DESCRIPTION, ++ ++ _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_pids pids; ++ 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; ++ }; ++} __attribute__((__aligned__(8))); ++ ++/** ++ * enum kdbus_msg_flags - type of message ++ * @KDBUS_MSG_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_NO_AUTO_START: Do not start a service if the addressed ++ * name is not currently active. This flag is ++ * not looked at by the kernel but only ++ * serves as hint for userspace implementations. ++ * @KDBUS_MSG_SIGNAL: Treat this message as signal ++ */ ++enum kdbus_msg_flags { ++ KDBUS_MSG_EXPECT_REPLY = 1ULL << 0, ++ KDBUS_MSG_NO_AUTO_START = 1ULL << 1, ++ KDBUS_MSG_SIGNAL = 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" ++ * ++ * Any payload-type is accepted. Common types will get added here once ++ * established. ++ */ ++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_*), userspace → kernel ++ * @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, and the send command is ++ * executed asynchronously, a kernel-generated message ++ * with an attached KDBUS_ITEM_REPLY_TIMEOUT item ++ * is sent to @src_id. For synchronously executed send ++ * command, the value denotes the maximum time the call ++ * blocks to wait for a reply. 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 ++ * @items: A list of kdbus_items containing the message payload ++ */ ++struct kdbus_msg { ++ __u64 size; ++ __u64 flags; ++ __s64 priority; ++ __u64 dst_id; ++ __u64 src_id; ++ __u64 payload_type; ++ __u64 cookie; ++ union { ++ __u64 timeout_ns; ++ __u64 cookie_reply; ++ }; ++ struct kdbus_item items[0]; ++} __attribute__((__aligned__(8))); ++ ++/** ++ * struct kdbus_msg_info - returned message container ++ * @offset: Offset of kdbus_msg slice in pool ++ * @msg_size: Copy of the kdbus_msg.size field ++ * @return_flags: Command return flags, kernel → userspace ++ */ ++struct kdbus_msg_info { ++ __u64 offset; ++ __u64 msg_size; ++ __u64 return_flags; ++} __attribute__((__aligned__(8))); ++ ++/** ++ * enum kdbus_send_flags - flags for sending messages ++ * @KDBUS_SEND_SYNC_REPLY: Wait for destination connection to ++ * reply to this message. The ++ * KDBUS_CMD_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_EXPECT_REPLY is set as well. ++ */ ++enum kdbus_send_flags { ++ KDBUS_SEND_SYNC_REPLY = 1ULL << 0, ++}; ++ ++/** ++ * struct kdbus_cmd_send - send message ++ * @size: Overall size of this structure ++ * @flags: Flags to change send behavior (KDBUS_SEND_*) ++ * @return_flags: Command return flags, kernel → userspace ++ * @msg_address: Storage address of the kdbus_msg to send ++ * @reply: Storage for message reply if KDBUS_SEND_SYNC_REPLY ++ * was given ++ * @items: Additional items for this command ++ */ ++struct kdbus_cmd_send { ++ __u64 size; ++ __u64 flags; ++ __u64 return_flags; ++ __u64 msg_address; ++ struct kdbus_msg_info 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, ++}; ++ ++/** ++ * enum kdbus_recv_return_flags - return flags for message receive commands ++ * @KDBUS_RECV_RETURN_INCOMPLETE_FDS: One or more file descriptors could not ++ * be installed. These descriptors in ++ * KDBUS_ITEM_FDS will carry the value -1. ++ * @KDBUS_RECV_RETURN_DROPPED_MSGS: There have been dropped messages since ++ * the last time a message was received. ++ * The 'dropped_msgs' counter contains the ++ * number of messages dropped pool ++ * overflows or other missed broadcasts. ++ */ ++enum kdbus_recv_return_flags { ++ KDBUS_RECV_RETURN_INCOMPLETE_FDS = 1ULL << 0, ++ KDBUS_RECV_RETURN_DROPPED_MSGS = 1ULL << 1, ++}; ++ ++/** ++ * struct kdbus_cmd_recv - struct to de-queue a buffered message ++ * @size: Overall size of this object ++ * @flags: KDBUS_RECV_* flags, userspace → kernel ++ * @return_flags: Command return flags, kernel → userspace ++ * @priority: Minimum priority of the messages to de-queue. Lowest ++ * values have the highest priority. ++ * @dropped_msgs: In case there were any dropped messages since the last ++ * time a message was received, this will be set to the ++ * number of lost messages and ++ * KDBUS_RECV_RETURN_DROPPED_MSGS will be set in ++ * 'return_flags'. This can only happen if the ioctl ++ * returns 0 or EAGAIN. ++ * @msg: Return storage for received message. ++ * @items: Additional items for this command. ++ * ++ * This struct is used with the KDBUS_CMD_RECV ioctl. ++ */ ++struct kdbus_cmd_recv { ++ __u64 size; ++ __u64 flags; ++ __u64 return_flags; ++ __s64 priority; ++ __u64 dropped_msgs; ++ struct kdbus_msg_info msg; ++ struct kdbus_item items[0]; ++} __attribute__((__aligned__(8))); ++ ++/** ++ * struct kdbus_cmd_free - struct to free a slice of memory in the pool ++ * @size: Overall size of this structure ++ * @flags: Flags for the free command, userspace → kernel ++ * @return_flags: Command return flags, kernel → userspace ++ * @offset: The offset of the memory slice, as returned by other ++ * ioctls ++ * @items: Additional items to modify the behavior ++ * ++ * This struct is used with the KDBUS_CMD_FREE ioctl. ++ */ ++struct kdbus_cmd_free { ++ __u64 size; ++ __u64 flags; ++ __u64 return_flags; ++ __u64 offset; ++ struct kdbus_item items[0]; ++} __attribute__((__aligned__(8))); ++ ++/** ++ * 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 acquire 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, ++}; ++ ++/** ++ * struct kdbus_cmd_hello - struct to say hello to kdbus ++ * @size: The total size of the structure ++ * @flags: Connection flags (KDBUS_HELLO_*), userspace → kernel ++ * @return_flags: Command return flags, kernel → userspace ++ * @attach_flags_send: Mask of metadata to attach to each message sent ++ * off by this connection (KDBUS_ATTACH_*) ++ * @attach_flags_recv: Mask of metadata to attach to each message receieved ++ * by the new connection (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 ++ * @offset: Pool offset where items are returned to report ++ * additional information about the bus and the newly ++ * created connection. ++ * @items_size: Size of buffer returned in the pool slice at @offset. ++ * @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 return_flags; ++ __u64 attach_flags_send; ++ __u64 attach_flags_recv; ++ __u64 bus_flags; ++ __u64 id; ++ __u64 pool_size; ++ __u64 offset; ++ __u64 items_size; ++ __u8 id128[16]; ++ struct kdbus_item items[0]; ++} __attribute__((__aligned__(8))); ++ ++/** ++ * struct kdbus_info - connection information ++ * @size: total size of the struct ++ * @id: 64bit object ID ++ * @flags: object creation flags ++ * @items: list of items ++ * ++ * 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]; ++} __attribute__((__aligned__(8))); ++ ++/** ++ * enum kdbus_list_flags - what to include into the returned list ++ * @KDBUS_LIST_UNIQUE: active connections ++ * @KDBUS_LIST_ACTIVATORS: activator connections ++ * @KDBUS_LIST_NAMES: known well-known names ++ * @KDBUS_LIST_QUEUED: queued-up names ++ */ ++enum kdbus_list_flags { ++ KDBUS_LIST_UNIQUE = 1ULL << 0, ++ KDBUS_LIST_NAMES = 1ULL << 1, ++ KDBUS_LIST_ACTIVATORS = 1ULL << 2, ++ KDBUS_LIST_QUEUED = 1ULL << 3, ++}; ++ ++/** ++ * struct kdbus_cmd_list - list connections ++ * @size: overall size of this object ++ * @flags: flags for the query (KDBUS_LIST_*), userspace → kernel ++ * @return_flags: command return flags, kernel → userspace ++ * @offset: Offset in the caller's pool buffer where an array of ++ * kdbus_info objects is stored. ++ * The user must use KDBUS_CMD_FREE to free the ++ * allocated memory. ++ * @list_size: size of returned list in bytes ++ * @items: Items for the command. Reserved for future use. ++ * ++ * This structure is used with the KDBUS_CMD_LIST ioctl. ++ */ ++struct kdbus_cmd_list { ++ __u64 size; ++ __u64 flags; ++ __u64 return_flags; ++ __u64 offset; ++ __u64 list_size; ++ struct kdbus_item items[0]; ++} __attribute__((__aligned__(8))); ++ ++/** ++ * struct kdbus_cmd_info - struct used for KDBUS_CMD_CONN_INFO ioctl ++ * @size: The total size of the struct ++ * @flags: Flags for this ioctl, userspace → kernel ++ * @return_flags: Command return 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. ++ * @attach_flags: Set of attach flags to specify the set of information ++ * to receive, userspace → kernel ++ * @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. ++ * @info_size: Output buffer to report size of data at @offset. ++ * @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 return_flags; ++ __u64 id; ++ __u64 attach_flags; ++ __u64 offset; ++ __u64 info_size; ++ 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 ++ * @flags: Flags for match command (KDBUS_MATCH_*), ++ * userspace → kernel ++ * @return_flags: Command return flags, kernel → userspace ++ * @cookie: Userspace supplied cookie. When removing, the cookie ++ * identifies the match to remove ++ * @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 flags; ++ __u64 return_flags; ++ __u64 cookie; ++ struct kdbus_item items[0]; ++} __attribute__((__aligned__(8))); ++ ++/** ++ * enum kdbus_make_flags - Flags for KDBUS_CMD_{BUS,ENDPOINT}_MAKE ++ * @KDBUS_MAKE_ACCESS_GROUP: Make the bus or endpoint node group-accessible ++ * @KDBUS_MAKE_ACCESS_WORLD: Make the bus or endpoint node world-accessible ++ */ ++enum kdbus_make_flags { ++ KDBUS_MAKE_ACCESS_GROUP = 1ULL << 0, ++ KDBUS_MAKE_ACCESS_WORLD = 1ULL << 1, ++}; ++ ++/** ++ * enum kdbus_name_flags - flags for KDBUS_CMD_NAME_ACQUIRE ++ * @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 - generic ioctl payload ++ * @size: Overall size of this structure ++ * @flags: Flags for this ioctl, userspace → kernel ++ * @return_flags: Ioctl return flags, kernel → userspace ++ * @items: Additional items to modify the behavior ++ * ++ * This is a generic ioctl payload object. It's used by all ioctls that only ++ * take flags and items as input. ++ */ ++struct kdbus_cmd { ++ __u64 size; ++ __u64 flags; ++ __u64 return_flags; ++ struct kdbus_item items[0]; ++} __attribute__((__aligned__(8))); ++ ++/** ++ * Ioctl API ++ * ++ * KDBUS_CMD_BUS_MAKE: After opening the "control" node, this command ++ * creates a new bus with the specified ++ * name. The bus is immediately shut down and ++ * cleaned up when the opened file descriptor is ++ * closed. ++ * ++ * 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_ENDPOINT_UPDATE: Update the properties of a custom enpoint. Used ++ * to update the policy. ++ * ++ * KDBUS_CMD_HELLO: By opening the bus node, a connection is ++ * created. After a HELLO the opened connection ++ * becomes an active peer on the bus. ++ * KDBUS_CMD_UPDATE: Update the properties of a connection. Used to ++ * update the metadata subscription mask and ++ * policy. ++ * 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_FREE: Release the allocated memory in the receiver's ++ * pool. ++ * 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_BUS_CREATOR_INFO: Retrieve information of the creator of the bus ++ * a connection is attached to. ++ * ++ * KDBUS_CMD_SEND: Send a message and pass data from userspace to ++ * the kernel. ++ * KDBUS_CMD_RECV: Receive a message from the kernel which is ++ * placed 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_LIST: Retrieve the list of all currently registered ++ * well-known and unique names. ++ * ++ * 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 { ++ /* bus owner (00-0f) */ ++ KDBUS_CMD_BUS_MAKE = _IOW(KDBUS_IOCTL_MAGIC, 0x00, ++ struct kdbus_cmd), ++ ++ /* endpoint owner (10-1f) */ ++ KDBUS_CMD_ENDPOINT_MAKE = _IOW(KDBUS_IOCTL_MAGIC, 0x10, ++ struct kdbus_cmd), ++ KDBUS_CMD_ENDPOINT_UPDATE = _IOW(KDBUS_IOCTL_MAGIC, 0x11, ++ struct kdbus_cmd), ++ ++ /* connection owner (80-ff) */ ++ KDBUS_CMD_HELLO = _IOWR(KDBUS_IOCTL_MAGIC, 0x80, ++ struct kdbus_cmd_hello), ++ KDBUS_CMD_UPDATE = _IOW(KDBUS_IOCTL_MAGIC, 0x81, ++ struct kdbus_cmd), ++ KDBUS_CMD_BYEBYE = _IOW(KDBUS_IOCTL_MAGIC, 0x82, ++ struct kdbus_cmd), ++ KDBUS_CMD_FREE = _IOW(KDBUS_IOCTL_MAGIC, 0x83, ++ struct kdbus_cmd_free), ++ KDBUS_CMD_CONN_INFO = _IOR(KDBUS_IOCTL_MAGIC, 0x84, ++ struct kdbus_cmd_info), ++ KDBUS_CMD_BUS_CREATOR_INFO = _IOR(KDBUS_IOCTL_MAGIC, 0x85, ++ struct kdbus_cmd_info), ++ KDBUS_CMD_LIST = _IOR(KDBUS_IOCTL_MAGIC, 0x86, ++ struct kdbus_cmd_list), ++ ++ KDBUS_CMD_SEND = _IOW(KDBUS_IOCTL_MAGIC, 0x90, ++ struct kdbus_cmd_send), ++ KDBUS_CMD_RECV = _IOR(KDBUS_IOCTL_MAGIC, 0x91, ++ struct kdbus_cmd_recv), ++ ++ KDBUS_CMD_NAME_ACQUIRE = _IOW(KDBUS_IOCTL_MAGIC, 0xa0, ++ struct kdbus_cmd), ++ KDBUS_CMD_NAME_RELEASE = _IOW(KDBUS_IOCTL_MAGIC, 0xa1, ++ struct kdbus_cmd), ++ ++ KDBUS_CMD_MATCH_ADD = _IOW(KDBUS_IOCTL_MAGIC, 0xb0, ++ struct kdbus_cmd_match), ++ KDBUS_CMD_MATCH_REMOVE = _IOW(KDBUS_IOCTL_MAGIC, 0xb1, ++ struct kdbus_cmd_match), ++}; ++ ++#endif /* _KDBUS_UAPI_H_ */ diff --git a/kdbus-add-walk-through-user-space-example.patch b/kdbus-add-walk-through-user-space-example.patch new file mode 100644 index 000000000..454f4cc02 --- /dev/null +++ b/kdbus-add-walk-through-user-space-example.patch @@ -0,0 +1,1538 @@ +From: Daniel Mack <daniel@zonque.org> +Date: Thu, 26 Feb 2015 21:06:38 +0100 +Subject: [PATCH] kdbus: add walk-through user space example + +Provide a walk-through example that explains how to use the low-level +ioctl API that kdbus offers. This example is meant to be useful for +developers who want to gain a in-depth understanding of how the kdbus +API works by reading a well-documented real-world example. + +This program computes prime-numbers based on the sieve of Eratosthenes. +The master sets up a shared memory region and spawns workers which clear +out the non-primes. The master reacts to keyboard input and to +client-requests to control what each worker does. Note that this is in +no way meant as efficient way to compute primes. It should only serve as +example how a master/worker concept can be implemented with kdbus used +as control messages. + +The main process is called the 'master'. It creates a new, private bus +which will be used between the master and its workers to communicate. +The master then spawns a fixed number of workers. Whenever a worker dies +(detected via SIGCHLD), the master spawns a new worker. When done, the +master waits for all workers to exit, prints a status report and exits +itself. + +The master process does *not* keep track of its workers. Instead, this +example implements a PULL model. That is, the master acquires a +well-known name on the bus which each worker uses to request tasks from +the master. If there are no more tasks, the master will return an empty +task-list, which casues a worker to exit immediately. + +As tasks can be computationally expensive, we support cancellation. +Whenever the master process is interrupted, it will drop its well-known +name on the bus. This causes kdbus to broadcast a name-change +notification. The workers check for broadcast messages regularly and +will exit if they receive one. + +Signed-off-by: Daniel Mack <daniel@zonque.org> +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Djalal Harouni <tixxdz@opendz.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + samples/Makefile | 3 +- + samples/kdbus/.gitignore | 1 + + samples/kdbus/Makefile | 10 + + samples/kdbus/kdbus-api.h | 114 ++++ + samples/kdbus/kdbus-workers.c | 1326 +++++++++++++++++++++++++++++++++++++++++ + 5 files changed, 1453 insertions(+), 1 deletion(-) + create mode 100644 samples/kdbus/.gitignore + create mode 100644 samples/kdbus/Makefile + create mode 100644 samples/kdbus/kdbus-api.h + create mode 100644 samples/kdbus/kdbus-workers.c + +diff --git a/samples/Makefile b/samples/Makefile +index f00257bcc5a7..f0ad51e5b342 100644 +--- a/samples/Makefile ++++ b/samples/Makefile +@@ -1,4 +1,5 @@ + # Makefile for Linux samples code + + obj-$(CONFIG_SAMPLES) += kobject/ kprobes/ trace_events/ livepatch/ \ +- hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ seccomp/ ++ hw_breakpoint/ kfifo/ kdb/ kdbus/ hidraw/ rpmsg/ \ ++ seccomp/ +diff --git a/samples/kdbus/.gitignore b/samples/kdbus/.gitignore +new file mode 100644 +index 000000000000..ee07d9857086 +--- /dev/null ++++ b/samples/kdbus/.gitignore +@@ -0,0 +1 @@ ++kdbus-workers +diff --git a/samples/kdbus/Makefile b/samples/kdbus/Makefile +new file mode 100644 +index 000000000000..d009025369f4 +--- /dev/null ++++ b/samples/kdbus/Makefile +@@ -0,0 +1,10 @@ ++# kbuild trick to avoid linker error. Can be omitted if a module is built. ++obj- := dummy.o ++ ++hostprogs-y += kdbus-workers ++ ++always := $(hostprogs-y) ++ ++HOSTCFLAGS_kdbus-workers.o += \ ++ -I$(objtree)/usr/include/ \ ++ -I$(objtree)/include/uapi/ +diff --git a/samples/kdbus/kdbus-api.h b/samples/kdbus/kdbus-api.h +new file mode 100644 +index 000000000000..5ed5907c5cb4 +--- /dev/null ++++ b/samples/kdbus/kdbus-api.h +@@ -0,0 +1,114 @@ ++#ifndef KDBUS_API_H ++#define KDBUS_API_H ++ ++#include <sys/ioctl.h> ++#include <linux/kdbus.h> ++ ++#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_FOREACH(iter, first, _size) \ ++ for (iter = (first); \ ++ ((uint8_t *)(iter) < (uint8_t *)(first) + (_size)) && \ ++ ((uint8_t *)(iter) >= (uint8_t *)(first)); \ ++ iter = (void*)(((uint8_t *)iter) + KDBUS_ALIGN8((iter)->size))) ++ ++static inline int kdbus_cmd_bus_make(int control_fd, struct kdbus_cmd *cmd) ++{ ++ int ret = ioctl(control_fd, KDBUS_CMD_BUS_MAKE, cmd); ++ return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; ++} ++ ++static inline int kdbus_cmd_endpoint_make(int bus_fd, struct kdbus_cmd *cmd) ++{ ++ int ret = ioctl(bus_fd, KDBUS_CMD_ENDPOINT_MAKE, cmd); ++ return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; ++} ++ ++static inline int kdbus_cmd_endpoint_update(int ep_fd, struct kdbus_cmd *cmd) ++{ ++ int ret = ioctl(ep_fd, KDBUS_CMD_ENDPOINT_UPDATE, cmd); ++ return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; ++} ++ ++static inline int kdbus_cmd_hello(int bus_fd, struct kdbus_cmd_hello *cmd) ++{ ++ int ret = ioctl(bus_fd, KDBUS_CMD_HELLO, cmd); ++ return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; ++} ++ ++static inline int kdbus_cmd_update(int fd, struct kdbus_cmd *cmd) ++{ ++ int ret = ioctl(fd, KDBUS_CMD_UPDATE, cmd); ++ return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; ++} ++ ++static inline int kdbus_cmd_byebye(int conn_fd, struct kdbus_cmd *cmd) ++{ ++ int ret = ioctl(conn_fd, KDBUS_CMD_BYEBYE, cmd); ++ return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; ++} ++ ++static inline int kdbus_cmd_free(int conn_fd, struct kdbus_cmd_free *cmd) ++{ ++ int ret = ioctl(conn_fd, KDBUS_CMD_FREE, cmd); ++ return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; ++} ++ ++static inline int kdbus_cmd_conn_info(int conn_fd, struct kdbus_cmd_info *cmd) ++{ ++ int ret = ioctl(conn_fd, KDBUS_CMD_CONN_INFO, cmd); ++ return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; ++} ++ ++static inline int kdbus_cmd_bus_creator_info(int conn_fd, struct kdbus_cmd_info *cmd) ++{ ++ int ret = ioctl(conn_fd, KDBUS_CMD_BUS_CREATOR_INFO, cmd); ++ return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; ++} ++ ++static inline int kdbus_cmd_list(int fd, struct kdbus_cmd_list *cmd) ++{ ++ int ret = ioctl(fd, KDBUS_CMD_LIST, cmd); ++ return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; ++} ++ ++static inline int kdbus_cmd_send(int conn_fd, struct kdbus_cmd_send *cmd) ++{ ++ int ret = ioctl(conn_fd, KDBUS_CMD_SEND, cmd); ++ return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; ++} ++ ++static inline int kdbus_cmd_recv(int conn_fd, struct kdbus_cmd_recv *cmd) ++{ ++ int ret = ioctl(conn_fd, KDBUS_CMD_RECV, cmd); ++ return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; ++} ++ ++static inline int kdbus_cmd_name_acquire(int conn_fd, struct kdbus_cmd *cmd) ++{ ++ int ret = ioctl(conn_fd, KDBUS_CMD_NAME_ACQUIRE, cmd); ++ return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; ++} ++ ++static inline int kdbus_cmd_name_release(int conn_fd, struct kdbus_cmd *cmd) ++{ ++ int ret = ioctl(conn_fd, KDBUS_CMD_NAME_RELEASE, cmd); ++ return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; ++} ++ ++static inline int kdbus_cmd_match_add(int conn_fd, struct kdbus_cmd_match *cmd) ++{ ++ int ret = ioctl(conn_fd, KDBUS_CMD_MATCH_ADD, cmd); ++ return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; ++} ++ ++static inline int kdbus_cmd_match_remove(int conn_fd, struct kdbus_cmd_match *cmd) ++{ ++ int ret = ioctl(conn_fd, KDBUS_CMD_MATCH_REMOVE, cmd); ++ return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; ++} ++ ++#endif /* KDBUS_API_H */ +diff --git a/samples/kdbus/kdbus-workers.c b/samples/kdbus/kdbus-workers.c +new file mode 100644 +index 000000000000..d1d8f7a7697b +--- /dev/null ++++ b/samples/kdbus/kdbus-workers.c +@@ -0,0 +1,1326 @@ ++/* ++ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> ++ * ++ * 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. ++ */ ++ ++/* ++ * Example: Workers ++ * This program computes prime-numbers based on the sieve of Eratosthenes. The ++ * master sets up a shared memory region and spawns workers which clear out the ++ * non-primes. The master reacts to keyboard input and to client-requests to ++ * control what each worker does. Note that this is in no way meant as efficient ++ * way to compute primes. It should only serve as example how a master/worker ++ * concept can be implemented with kdbus used as control messages. ++ * ++ * The main process is called the 'master'. It creates a new, private bus which ++ * will be used between the master and its workers to communicate. The master ++ * then spawns a fixed number of workers. Whenever a worker dies (detected via ++ * SIGCHLD), the master spawns a new worker. When done, the master waits for all ++ * workers to exit, prints a status report and exits itself. ++ * ++ * The master process does *not* keep track of its workers. Instead, this ++ * example implements a PULL model. That is, the master acquires a well-known ++ * name on the bus which each worker uses to request tasks from the master. If ++ * there are no more tasks, the master will return an empty task-list, which ++ * casues a worker to exit immediately. ++ * ++ * As tasks can be computationally expensive, we support cancellation. Whenever ++ * the master process is interrupted, it will drop its well-known name on the ++ * bus. This causes kdbus to broadcast a name-change notification. The workers ++ * check for broadcast messages regularly and will exit if they receive one. ++ * ++ * This example exists of 4 objects: ++ * * master: The master object contains the context of the master process. This ++ * process manages the prime-context, spawns workers and assigns ++ * prime-ranges to each worker to compute. ++ * The master itself does not do any prime-computations itself. ++ * * child: The child object contains the context of a worker. It inherits the ++ * prime context from its parent (the master) and then creates a new ++ * bus context to request prime-ranges to compute. ++ * * prime: The "prime" object is used to abstract how we compute primes. When ++ * allocated, it prepares a memory region to hold 1 bit for each ++ * natural number up to a fixed maximum ('MAX_PRIMES'). ++ * The memory region is backed by a memfd which we share between ++ * processes. Each worker now gets assigned a range of natural ++ * numbers which it clears multiples of off the memory region. The ++ * master process is responsible of distributing all natural numbers ++ * up to the fixed maximum to its workers. ++ * * bus: The bus object is an abstraction of the kdbus API. It is pretty ++ * straightfoward and only manages the connection-fd plus the ++ * memory-mapped pool in a single object. ++ * ++ * This example is in reversed order, which should make it easier to read ++ * top-down, but requires some forward-declarations. Just ignore those. ++ */ ++ ++#include <ctype.h> ++#include <errno.h> ++#include <fcntl.h> ++#include <linux/memfd.h> ++#include <signal.h> ++#include <stdbool.h> ++#include <stddef.h> ++#include <stdint.h> ++#include <stdio.h> ++#include <stdlib.h> ++#include <string.h> ++#include <sys/mman.h> ++#include <sys/poll.h> ++#include <sys/signalfd.h> ++#include <sys/syscall.h> ++#include <sys/time.h> ++#include <sys/wait.h> ++#include <time.h> ++#include <unistd.h> ++#include "kdbus-api.h" ++ ++/* FORWARD DECLARATIONS */ ++ ++#define POOL_SIZE (16 * 1024 * 1024) ++#define MAX_PRIMES (2UL << 24) ++#define WORKER_COUNT (16) ++#define PRIME_STEPS (65536 * 4) ++ ++static const char *arg_busname = "example-workers"; ++static const char *arg_modname = "kdbus"; ++static const char *arg_master = "org.freedesktop.master"; ++ ++static int err_assert(int r_errno, const char *msg, const char *func, int line, ++ const char *file) ++{ ++ r_errno = (r_errno != 0) ? -abs(r_errno) : -EFAULT; ++ if (r_errno < 0) { ++ errno = -r_errno; ++ fprintf(stderr, "ERR: %s: %m (%s:%d in %s)\n", ++ msg, func, line, file); ++ } ++ return r_errno; ++} ++ ++#define err_r(_r, _msg) err_assert((_r), (_msg), __func__, __LINE__, __FILE__) ++#define err(_msg) err_r(errno, (_msg)) ++ ++struct prime; ++struct bus; ++struct master; ++struct child; ++ ++struct prime { ++ int fd; ++ uint8_t *area; ++ size_t max; ++ size_t done; ++ size_t status; ++}; ++ ++static int prime_new(struct prime **out); ++static void prime_free(struct prime *p); ++static bool prime_done(struct prime *p); ++static void prime_consume(struct prime *p, size_t amount); ++static int prime_run(struct prime *p, struct bus *cancel, size_t number); ++static void prime_print(struct prime *p); ++ ++struct bus { ++ int fd; ++ uint8_t *pool; ++}; ++ ++static int bus_open_connection(struct bus **out, uid_t uid, const char *name, ++ uint64_t recv_flags); ++static void bus_close_connection(struct bus *b); ++static void bus_poool_free_slice(struct bus *b, uint64_t offset); ++static int bus_acquire_name(struct bus *b, const char *name); ++static int bus_install_name_loss_match(struct bus *b, const char *name); ++static int bus_poll(struct bus *b); ++static int bus_make(uid_t uid, const char *name); ++ ++struct master { ++ size_t n_workers; ++ size_t max_workers; ++ ++ int signal_fd; ++ int control_fd; ++ ++ struct prime *prime; ++ struct bus *bus; ++}; ++ ++static int master_new(struct master **out); ++static void master_free(struct master *m); ++static int master_run(struct master *m); ++static int master_poll(struct master *m); ++static int master_handle_stdin(struct master *m); ++static int master_handle_signal(struct master *m); ++static int master_handle_bus(struct master *m); ++static int master_reply(struct master *m, const struct kdbus_msg *msg); ++static int master_waitpid(struct master *m); ++static int master_spawn(struct master *m); ++ ++struct child { ++ struct bus *bus; ++ struct prime *prime; ++}; ++ ++static int child_new(struct child **out, struct prime *p); ++static void child_free(struct child *c); ++static int child_run(struct child *c); ++ ++/* END OF FORWARD DECLARATIONS */ ++ ++/* ++ * This is the main entrypoint of this example. It is pretty straightforward. We ++ * create a master object, run the computation, print a status report and then ++ * exit. Nothing particularly interesting here, so lets look into the master ++ * object... ++ */ ++int main(int argc, char **argv) ++{ ++ struct master *m = NULL; ++ int r; ++ ++ r = master_new(&m); ++ if (r < 0) ++ goto out; ++ ++ r = master_run(m); ++ if (r < 0) ++ goto out; ++ ++ if (0) ++ prime_print(m->prime); ++ ++out: ++ master_free(m); ++ if (r < 0 && r != -EINTR) ++ fprintf(stderr, "failed\n"); ++ else ++ fprintf(stderr, "done\n"); ++ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; ++} ++ ++/* ++ * ...this will allocate a new master context. It keeps track of the current ++ * number of children/workers that are running, manages a signalfd to track ++ * SIGCHLD, and creates a private kdbus bus. Afterwards, it opens its connection ++ * to the bus and acquires a well known-name (arg_master). ++ */ ++static int master_new(struct master **out) ++{ ++ struct master *m; ++ sigset_t smask; ++ int r; ++ ++ m = calloc(1, sizeof(*m)); ++ if (!m) ++ return err("cannot allocate master"); ++ ++ m->max_workers = WORKER_COUNT; ++ m->signal_fd = -1; ++ m->control_fd = -1; ++ ++ /* Block SIGINT and SIGCHLD signals */ ++ sigemptyset(&smask); ++ sigaddset(&smask, SIGINT); ++ sigaddset(&smask, SIGCHLD); ++ sigprocmask(SIG_BLOCK, &smask, NULL); ++ ++ m->signal_fd = signalfd(-1, &smask, SFD_CLOEXEC); ++ if (m->signal_fd < 0) { ++ r = err("cannot create signalfd"); ++ goto error; ++ } ++ ++ r = prime_new(&m->prime); ++ if (r < 0) ++ goto error; ++ ++ m->control_fd = bus_make(getuid(), arg_busname); ++ if (m->control_fd < 0) { ++ r = m->control_fd; ++ goto error; ++ } ++ ++ /* ++ * Open a bus connection for the master, and require each received ++ * message to have a metadata item of type KDBUS_ITEM_PIDS attached. ++ * The current UID is needed to compute the name of the bus node to ++ * connect to. ++ */ ++ r = bus_open_connection(&m->bus, getuid(), ++ arg_busname, KDBUS_ATTACH_PIDS); ++ if (r < 0) ++ goto error; ++ ++ /* ++ * Acquire a well-known name on the bus, so children can address ++ * messages to the master using KDBUS_DST_ID_NAME as destination-ID ++ * of messages. ++ */ ++ r = bus_acquire_name(m->bus, arg_master); ++ if (r < 0) ++ goto error; ++ ++ *out = m; ++ return 0; ++ ++error: ++ master_free(m); ++ return r; ++} ++ ++/* pretty straightforward destructor of a master object */ ++static void master_free(struct master *m) ++{ ++ if (!m) ++ return; ++ ++ bus_close_connection(m->bus); ++ if (m->control_fd >= 0) ++ close(m->control_fd); ++ prime_free(m->prime); ++ if (m->signal_fd >= 0) ++ close(m->signal_fd); ++ free(m); ++} ++ ++static int master_run(struct master *m) ++{ ++ int res, r = 0; ++ ++ while (!prime_done(m->prime)) { ++ while (m->n_workers < m->max_workers) { ++ r = master_spawn(m); ++ if (r < 0) ++ break; ++ } ++ ++ r = master_poll(m); ++ if (r < 0) ++ break; ++ } ++ ++ if (r < 0) { ++ bus_close_connection(m->bus); ++ m->bus = NULL; ++ } ++ ++ while (m->n_workers > 0) { ++ res = master_poll(m); ++ if (res < 0) { ++ if (m->bus) { ++ bus_close_connection(m->bus); ++ m->bus = NULL; ++ } ++ r = res; ++ } ++ } ++ ++ return r == -EINTR ? 0 : r; ++} ++ ++static int master_poll(struct master *m) ++{ ++ struct pollfd fds[3] = {}; ++ int r = 0, n = 0; ++ ++ /* ++ * Add stdin, the eventfd and the connection owner file descriptor to ++ * the pollfd table, and handle incoming traffic on the latter in ++ * master_handle_bus(). ++ */ ++ fds[n].fd = STDIN_FILENO; ++ fds[n++].events = POLLIN; ++ fds[n].fd = m->signal_fd; ++ fds[n++].events = POLLIN; ++ if (m->bus) { ++ fds[n].fd = m->bus->fd; ++ fds[n++].events = POLLIN; ++ } ++ ++ r = poll(fds, n, -1); ++ if (r < 0) ++ return err("poll() failed"); ++ ++ if (fds[0].revents & POLLIN) ++ r = master_handle_stdin(m); ++ else if (fds[0].revents) ++ r = err("ERR/HUP on stdin"); ++ if (r < 0) ++ return r; ++ ++ if (fds[1].revents & POLLIN) ++ r = master_handle_signal(m); ++ else if (fds[1].revents) ++ r = err("ERR/HUP on signalfd"); ++ if (r < 0) ++ return r; ++ ++ if (fds[2].revents & POLLIN) ++ r = master_handle_bus(m); ++ else if (fds[2].revents) ++ r = err("ERR/HUP on bus"); ++ ++ return r; ++} ++ ++static int master_handle_stdin(struct master *m) ++{ ++ char buf[128]; ++ ssize_t l; ++ int r = 0; ++ ++ l = read(STDIN_FILENO, buf, sizeof(buf)); ++ if (l < 0) ++ return err("cannot read stdin"); ++ if (l == 0) ++ return err_r(-EINVAL, "EOF on stdin"); ++ ++ while (l-- > 0) { ++ switch (buf[l]) { ++ case 'q': ++ /* quit */ ++ r = -EINTR; ++ break; ++ case '\n': ++ case ' ': ++ /* ignore */ ++ break; ++ default: ++ if (isgraph(buf[l])) ++ fprintf(stderr, "invalid input '%c'\n", buf[l]); ++ else ++ fprintf(stderr, "invalid input 0x%x\n", buf[l]); ++ break; ++ } ++ } ++ ++ return r; ++} ++ ++static int master_handle_signal(struct master *m) ++{ ++ struct signalfd_siginfo val; ++ ssize_t l; ++ ++ l = read(m->signal_fd, &val, sizeof(val)); ++ if (l < 0) ++ return err("cannot read signalfd"); ++ if (l != sizeof(val)) ++ return err_r(-EINVAL, "invalid data from signalfd"); ++ ++ switch (val.ssi_signo) { ++ case SIGCHLD: ++ return master_waitpid(m); ++ case SIGINT: ++ return err_r(-EINTR, "interrupted"); ++ default: ++ return err_r(-EINVAL, "caught invalid signal"); ++ } ++} ++ ++static int master_handle_bus(struct master *m) ++{ ++ struct kdbus_cmd_recv recv = { .size = sizeof(recv) }; ++ const struct kdbus_msg *msg = NULL; ++ const struct kdbus_item *item; ++ const struct kdbus_vec *vec = NULL; ++ int r = 0; ++ ++ /* ++ * To receive a message, the KDBUS_CMD_RECV ioctl is used. ++ * It takes an argument of type 'struct kdbus_cmd_recv', which ++ * will contain information on the received message when the call ++ * returns. See kdbus.message(7). ++ */ ++ r = kdbus_cmd_recv(m->bus->fd, &recv); ++ /* ++ * EAGAIN is returned when there is no message waiting on this ++ * connection. This is not an error - simply bail out. ++ */ ++ if (r == -EAGAIN) ++ return 0; ++ if (r < 0) ++ return err_r(r, "cannot receive message"); ++ ++ /* ++ * Messages received by a connection are stored inside the connection's ++ * pool, at an offset that has been returned in the 'recv' command ++ * struct above. The value describes the relative offset from the ++ * start address of the pool. A message is described with ++ * 'struct kdbus_msg'. See kdbus.message(7). ++ */ ++ msg = (void *)(m->bus->pool + recv.msg.offset); ++ ++ /* ++ * A messages describes its actual payload in an array of items. ++ * KDBUS_FOREACH() is a simple iterator that walks such an array. ++ * struct kdbus_msg has a field to denote its total size, which is ++ * needed to determine the number of items in the array. ++ */ ++ KDBUS_FOREACH(item, msg->items, ++ msg->size - offsetof(struct kdbus_msg, items)) { ++ /* ++ * An item of type PAYLOAD_OFF describes in-line memory ++ * stored in the pool at a described offset. That offset is ++ * relative to the start address of the message header. ++ * This example program only expects one single item of that ++ * type, remembers the struct kdbus_vec member of the item ++ * when it sees it, and bails out if there is more than one ++ * of them. ++ */ ++ if (item->type == KDBUS_ITEM_PAYLOAD_OFF) { ++ if (vec) { ++ r = err_r(-EEXIST, ++ "message with multiple vecs"); ++ break; ++ } ++ vec = &item->vec; ++ if (vec->size != 1) { ++ r = err_r(-EINVAL, "invalid message size"); ++ break; ++ } ++ ++ /* ++ * MEMFDs are transported as items of type PAYLOAD_MEMFD. ++ * If such an item is attached, a new file descriptor was ++ * installed into the task when KDBUS_CMD_RECV was called, and ++ * its number is stored in item->memfd.fd. ++ * Implementers *must* handle this item type and close the ++ * file descriptor when no longer needed in order to prevent ++ * file descriptor exhaustion. This example program just bails ++ * out with an error in this case, as memfds are not expected ++ * in this context. ++ */ ++ } else if (item->type == KDBUS_ITEM_PAYLOAD_MEMFD) { ++ r = err_r(-EINVAL, "message with memfd"); ++ break; ++ } ++ } ++ if (r < 0) ++ goto exit; ++ if (!vec) { ++ r = err_r(-EINVAL, "empty message"); ++ goto exit; ++ } ++ ++ switch (*((const uint8_t *)msg + vec->offset)) { ++ case 'r': { ++ r = master_reply(m, msg); ++ break; ++ } ++ default: ++ r = err_r(-EINVAL, "invalid message type"); ++ break; ++ } ++ ++exit: ++ /* ++ * We are done with the memory slice that was given to us through ++ * recv.msg.offset. Tell the kernel it can use it for other content ++ * in the future. See kdbus.pool(7). ++ */ ++ bus_poool_free_slice(m->bus, recv.msg.offset); ++ return r; ++} ++ ++static int master_reply(struct master *m, const struct kdbus_msg *msg) ++{ ++ struct kdbus_cmd_send cmd; ++ struct kdbus_item *item; ++ struct kdbus_msg *reply; ++ size_t size, status, p[2]; ++ int r; ++ ++ /* ++ * This functions sends a message over kdbus. To do this, it uses the ++ * KDBUS_CMD_SEND ioctl, which takes a command struct argument of type ++ * 'struct kdbus_cmd_send'. This struct stores a pointer to the actual ++ * message to send. See kdbus.message(7). ++ */ ++ p[0] = m->prime->done; ++ p[1] = prime_done(m->prime) ? 0 : PRIME_STEPS; ++ ++ size = sizeof(*reply); ++ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec)); ++ ++ /* Prepare the message to send */ ++ reply = alloca(size); ++ memset(reply, 0, size); ++ reply->size = size; ++ ++ /* Each message has a cookie that can be used to send replies */ ++ reply->cookie = 1; ++ ++ /* The payload_type is arbitrary, but it must be non-zero */ ++ reply->payload_type = 0xdeadbeef; ++ ++ /* ++ * We are sending a reply. Let the kernel know the cookie of the ++ * message we are replying to. ++ */ ++ reply->cookie_reply = msg->cookie; ++ ++ /* ++ * Messages can either be directed to a well-known name (stored as ++ * string) or to a unique name (stored as number). This example does ++ * the latter. If the message would be directed to a well-known name ++ * instead, the message's dst_id field would be set to ++ * KDBUS_DST_ID_NAME, and the name would be attaches in an item of type ++ * KDBUS_ITEM_DST_NAME. See below for an example, and also refer to ++ * kdbus.message(7). ++ */ ++ reply->dst_id = msg->src_id; ++ ++ /* Our message has exactly one item to store its payload */ ++ item = reply->items; ++ item->type = KDBUS_ITEM_PAYLOAD_VEC; ++ item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec); ++ item->vec.address = (uintptr_t)p; ++ item->vec.size = sizeof(p); ++ ++ /* ++ * Now prepare the command struct, and reference the message we want ++ * to send. ++ */ ++ memset(&cmd, 0, sizeof(cmd)); ++ cmd.size = sizeof(cmd); ++ cmd.msg_address = (uintptr_t)reply; ++ ++ /* ++ * Finally, employ the command on the connection owner ++ * file descriptor. ++ */ ++ r = kdbus_cmd_send(m->bus->fd, &cmd); ++ if (r < 0) ++ return err_r(r, "cannot send reply"); ++ ++ if (p[1]) { ++ prime_consume(m->prime, p[1]); ++ status = m->prime->done * 10000 / m->prime->max; ++ if (status != m->prime->status) { ++ m->prime->status = status; ++ fprintf(stderr, "status: %7.3lf%%\n", ++ (double)status / 100); ++ } ++ } ++ ++ return 0; ++} ++ ++static int master_waitpid(struct master *m) ++{ ++ pid_t pid; ++ int r; ++ ++ while ((pid = waitpid(-1, &r, WNOHANG)) > 0) { ++ if (m->n_workers > 0) ++ --m->n_workers; ++ if (!WIFEXITED(r)) ++ r = err_r(-EINVAL, "child died unexpectedly"); ++ else if (WEXITSTATUS(r) != 0) ++ r = err_r(-WEXITSTATUS(r), "child failed"); ++ } ++ ++ return r; ++} ++ ++static int master_spawn(struct master *m) ++{ ++ struct child *c = NULL; ++ struct prime *p = NULL; ++ pid_t pid; ++ int r; ++ ++ /* Spawn off one child and call child_run() inside it */ ++ ++ pid = fork(); ++ if (pid < 0) ++ return err("cannot fork"); ++ if (pid > 0) { ++ /* parent */ ++ ++m->n_workers; ++ return 0; ++ } ++ ++ /* child */ ++ ++ p = m->prime; ++ m->prime = NULL; ++ master_free(m); ++ ++ r = child_new(&c, p); ++ if (r < 0) ++ goto exit; ++ ++ r = child_run(c); ++ ++exit: ++ child_free(c); ++ exit(abs(r)); ++} ++ ++static int child_new(struct child **out, struct prime *p) ++{ ++ struct child *c; ++ int r; ++ ++ c = calloc(1, sizeof(*c)); ++ if (!c) ++ return err("cannot allocate child"); ++ ++ c->prime = p; ++ ++ /* ++ * Open a connection to the bus and require each received message to ++ * carry a list of the well-known names the sendind connection currently ++ * owns. The current UID is needed in order to determine the name of the ++ * bus node to connect to. ++ */ ++ r = bus_open_connection(&c->bus, getuid(), ++ arg_busname, KDBUS_ATTACH_NAMES); ++ if (r < 0) ++ goto error; ++ ++ /* ++ * Install a kdbus match so the child's connection gets notified when ++ * the master loses its well-known name. ++ */ ++ r = bus_install_name_loss_match(c->bus, arg_master); ++ if (r < 0) ++ goto error; ++ ++ *out = c; ++ return 0; ++ ++error: ++ child_free(c); ++ return r; ++} ++ ++static void child_free(struct child *c) ++{ ++ if (!c) ++ return; ++ ++ bus_close_connection(c->bus); ++ prime_free(c->prime); ++ free(c); ++} ++ ++static int child_run(struct child *c) ++{ ++ struct kdbus_cmd_send cmd; ++ struct kdbus_item *item; ++ struct kdbus_vec *vec = NULL; ++ struct kdbus_msg *msg; ++ struct timespec spec; ++ size_t n, steps, size; ++ int r = 0; ++ ++ /* ++ * Let's send a message to the master and ask for work. To do this, ++ * we use the KDBUS_CMD_SEND ioctl, which takes an argument of type ++ * 'struct kdbus_cmd_send'. This struct stores a pointer to the actual ++ * message to send. See kdbus.message(7). ++ */ ++ size = sizeof(*msg); ++ size += KDBUS_ITEM_SIZE(strlen(arg_master) + 1); ++ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec)); ++ ++ msg = alloca(size); ++ memset(msg, 0, size); ++ msg->size = size; ++ ++ /* ++ * Tell the kernel that we expect a reply to this message. This means ++ * that ++ * ++ * a) The remote peer will gain temporary permission to talk to us ++ * even if it would not be allowed to normally. ++ * ++ * b) A timeout value is required. ++ * ++ * For asynchronous send commands, if no reply is received, we will ++ * get a kernel notification with an item of type ++ * KDBUS_ITEM_REPLY_TIMEOUT attached. ++ * ++ * For synchronous send commands (which this example does), the ++ * ioctl will block until a reply is received or the timeout is ++ * exceeded. ++ */ ++ msg->flags = KDBUS_MSG_EXPECT_REPLY; ++ ++ /* Set our cookie. Replies must use this cookie to send their reply. */ ++ msg->cookie = 1; ++ ++ /* The payload_type is arbitrary, but it must be non-zero */ ++ msg->payload_type = 0xdeadbeef; ++ ++ /* ++ * We are sending our message to the current owner of a well-known ++ * name. This makes an item of type KDBUS_ITEM_DST_NAME mandatory. ++ */ ++ msg->dst_id = KDBUS_DST_ID_NAME; ++ ++ /* ++ * Set the reply timeout to 5 seconds. Timeouts are always set in ++ * absolute timestamps, based con CLOCK_MONOTONIC. See kdbus.message(7). ++ */ ++ clock_gettime(CLOCK_MONOTONIC_COARSE, &spec); ++ msg->timeout_ns += (5 + spec.tv_sec) * 1000ULL * 1000ULL * 1000ULL; ++ msg->timeout_ns += spec.tv_nsec; ++ ++ /* ++ * Fill the appended items. First, set the well-known name of the ++ * destination we want to talk to. ++ */ ++ item = msg->items; ++ item->type = KDBUS_ITEM_DST_NAME; ++ item->size = KDBUS_ITEM_HEADER_SIZE + strlen(arg_master) + 1; ++ strcpy(item->str, arg_master); ++ ++ /* ++ * The 2nd item contains a vector to memory we want to send. It ++ * can be content of any type. In our case, we're sending a one-byte ++ * string only. The memory referenced by this item will be copied into ++ * the pool of the receveiver connection, and does not need to be ++ * valid after the command is employed. ++ */ ++ 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)"r"; ++ item->vec.size = 1; ++ ++ /* Set up the command struct and reference the message we prepared */ ++ memset(&cmd, 0, sizeof(cmd)); ++ cmd.size = sizeof(cmd); ++ cmd.msg_address = (uintptr_t)msg; ++ ++ /* ++ * The send commands knows a mode in which it will block until a ++ * reply to a message is received. This example uses that mode. ++ * The pool offset to the received reply will be stored in the command ++ * struct after the send command returned. See below. ++ */ ++ cmd.flags = KDBUS_SEND_SYNC_REPLY; ++ ++ /* ++ * Finally, employ the command on the connection owner ++ * file descriptor. ++ */ ++ r = kdbus_cmd_send(c->bus->fd, &cmd); ++ if (r == -ESRCH || r == -EPIPE || r == -ECONNRESET) ++ return 0; ++ if (r < 0) ++ return err_r(r, "cannot send request to master"); ++ ++ /* ++ * The command was sent with the KDBUS_SEND_SYNC_REPLY flag set, ++ * and returned successfully, which means that cmd.reply.offset now ++ * points to a message inside our connection's pool where the reply ++ * is found. This is equivalent to receiving the reply with ++ * KDBUS_CMD_RECV, but it doesn't require waiting for the reply with ++ * poll() and also saves the ioctl to receive the message. ++ */ ++ msg = (void *)(c->bus->pool + cmd.reply.offset); ++ ++ /* ++ * A messages describes its actual payload in an array of items. ++ * KDBUS_FOREACH() is a simple iterator that walks such an array. ++ * struct kdbus_msg has a field to denote its total size, which is ++ * needed to determine the number of items in the array. ++ */ ++ KDBUS_FOREACH(item, msg->items, ++ msg->size - offsetof(struct kdbus_msg, items)) { ++ /* ++ * An item of type PAYLOAD_OFF describes in-line memory ++ * stored in the pool at a described offset. That offset is ++ * relative to the start address of the message header. ++ * This example program only expects one single item of that ++ * type, remembers the struct kdbus_vec member of the item ++ * when it sees it, and bails out if there is more than one ++ * of them. ++ */ ++ if (item->type == KDBUS_ITEM_PAYLOAD_OFF) { ++ if (vec) { ++ r = err_r(-EEXIST, ++ "message with multiple vecs"); ++ break; ++ } ++ vec = &item->vec; ++ if (vec->size != 2 * sizeof(size_t)) { ++ r = err_r(-EINVAL, "invalid message size"); ++ break; ++ } ++ /* ++ * MEMFDs are transported as items of type PAYLOAD_MEMFD. ++ * If such an item is attached, a new file descriptor was ++ * installed into the task when KDBUS_CMD_RECV was called, and ++ * its number is stored in item->memfd.fd. ++ * Implementers *must* handle this item type close the ++ * file descriptor when no longer needed in order to prevent ++ * file descriptor exhaustion. This example program just bails ++ * out with an error in this case, as memfds are not expected ++ * in this context. ++ */ ++ } else if (item->type == KDBUS_ITEM_PAYLOAD_MEMFD) { ++ r = err_r(-EINVAL, "message with memfd"); ++ break; ++ } ++ } ++ if (r < 0) ++ goto exit; ++ if (!vec) { ++ r = err_r(-EINVAL, "empty message"); ++ goto exit; ++ } ++ ++ n = ((size_t *)((const uint8_t *)msg + vec->offset))[0]; ++ steps = ((size_t *)((const uint8_t *)msg + vec->offset))[1]; ++ ++ while (steps-- > 0) { ++ ++n; ++ r = prime_run(c->prime, c->bus, n); ++ if (r < 0) ++ break; ++ r = bus_poll(c->bus); ++ if (r != 0) { ++ r = r < 0 ? r : -EINTR; ++ break; ++ } ++ } ++ ++exit: ++ /* ++ * We are done with the memory slice that was given to us through ++ * cmd.reply.offset. Tell the kernel it can use it for other content ++ * in the future. See kdbus.pool(7). ++ */ ++ bus_poool_free_slice(c->bus, cmd.reply.offset); ++ return r; ++} ++ ++/* ++ * Prime Computation ++ * ++ */ ++ ++static int prime_new(struct prime **out) ++{ ++ struct prime *p; ++ int r; ++ ++ p = calloc(1, sizeof(*p)); ++ if (!p) ++ return err("cannot allocate prime memory"); ++ ++ p->fd = -1; ++ p->area = MAP_FAILED; ++ p->max = MAX_PRIMES; ++ ++ /* ++ * Prepare and map a memfd to store the bit-fields for the number ++ * ranges we want to perform the prime detection on. ++ */ ++ p->fd = syscall(__NR_memfd_create, "prime-area", MFD_CLOEXEC); ++ if (p->fd < 0) { ++ r = err("cannot create memfd"); ++ goto error; ++ } ++ ++ r = ftruncate(p->fd, p->max / 8 + 1); ++ if (r < 0) { ++ r = err("cannot ftruncate area"); ++ goto error; ++ } ++ ++ p->area = mmap(NULL, p->max / 8 + 1, PROT_READ | PROT_WRITE, ++ MAP_SHARED, p->fd, 0); ++ if (p->area == MAP_FAILED) { ++ r = err("cannot mmap memfd"); ++ goto error; ++ } ++ ++ *out = p; ++ return 0; ++ ++error: ++ prime_free(p); ++ return r; ++} ++ ++static void prime_free(struct prime *p) ++{ ++ if (!p) ++ return; ++ ++ if (p->area != MAP_FAILED) ++ munmap(p->area, p->max / 8 + 1); ++ if (p->fd >= 0) ++ close(p->fd); ++ free(p); ++} ++ ++static bool prime_done(struct prime *p) ++{ ++ return p->done >= p->max; ++} ++ ++static void prime_consume(struct prime *p, size_t amount) ++{ ++ p->done += amount; ++} ++ ++static int prime_run(struct prime *p, struct bus *cancel, size_t number) ++{ ++ size_t i, n = 0; ++ int r; ++ ++ if (number < 2 || number > 65535) ++ return 0; ++ ++ for (i = number * number; ++ i < p->max && i > number; ++ i += number) { ++ p->area[i / 8] |= 1 << (i % 8); ++ ++ if (!(++n % (1 << 20))) { ++ r = bus_poll(cancel); ++ if (r != 0) ++ return r < 0 ? r : -EINTR; ++ } ++ } ++ ++ return 0; ++} ++ ++static void prime_print(struct prime *p) ++{ ++ size_t i, l = 0; ++ ++ fprintf(stderr, "PRIMES:"); ++ for (i = 0; i < p->max; ++i) { ++ if (!(p->area[i / 8] & (1 << (i % 8)))) ++ fprintf(stderr, "%c%7zu", !(l++ % 16) ? '\n' : ' ', i); ++ } ++ fprintf(stderr, "\nEND\n"); ++} ++ ++static int bus_open_connection(struct bus **out, uid_t uid, const char *name, ++ uint64_t recv_flags) ++{ ++ struct kdbus_cmd_hello hello; ++ char path[128]; ++ struct bus *b; ++ int r; ++ ++ /* ++ * The 'bus' object is our representation of a kdbus connection which ++ * stores two details: the connection owner file descriptor, and the ++ * mmap()ed memory of its associated pool. See kdbus.connection(7) and ++ * kdbus.pool(7). ++ */ ++ b = calloc(1, sizeof(*b)); ++ if (!b) ++ return err("cannot allocate bus memory"); ++ ++ b->fd = -1; ++ b->pool = MAP_FAILED; ++ ++ /* Compute the name of the bus node to connect to. */ ++ snprintf(path, sizeof(path), "/sys/fs/%s/%lu-%s/bus", ++ arg_modname, (unsigned long)uid, name); ++ b->fd = open(path, O_RDWR | O_CLOEXEC); ++ if (b->fd < 0) { ++ r = err("cannot open bus"); ++ goto error; ++ } ++ ++ /* ++ * To make a connection to the bus, the KDBUS_CMD_HELLO ioctl is used. ++ * It takes an argument of type 'struct kdbus_cmd_hello'. ++ */ ++ memset(&hello, 0, sizeof(hello)); ++ hello.size = sizeof(hello); ++ ++ /* ++ * Specify a mask of metadata attach flags, describing metadata items ++ * that this new connection allows to be sent. ++ */ ++ hello.attach_flags_send = _KDBUS_ATTACH_ALL; ++ ++ /* ++ * Specify a mask of metadata attach flags, describing metadata items ++ * that this new connection wants to be receive along with each message. ++ */ ++ hello.attach_flags_recv = recv_flags; ++ ++ /* ++ * A connection may choose the size of its pool, but the number has to ++ * comply with two rules: a) it must be greater than 0, and b) it must ++ * be a mulitple of PAGE_SIZE. See kdbus.pool(7). ++ */ ++ hello.pool_size = POOL_SIZE; ++ ++ /* ++ * Now employ the command on the file descriptor opened above. ++ * This command will turn the file descriptor into a connection-owner ++ * file descriptor that controls the life-time of the connection; once ++ * it's closed, the connection is shut down. ++ */ ++ r = kdbus_cmd_hello(b->fd, &hello); ++ if (r < 0) { ++ err_r(r, "HELLO failed"); ++ goto error; ++ } ++ ++ bus_poool_free_slice(b, hello.offset); ++ ++ /* ++ * Map the pool of the connection. Its size has been set in the ++ * command struct above. See kdbus.pool(7). ++ */ ++ b->pool = mmap(NULL, POOL_SIZE, PROT_READ, MAP_SHARED, b->fd, 0); ++ if (b->pool == MAP_FAILED) { ++ r = err("cannot mmap pool"); ++ goto error; ++ } ++ ++ *out = b; ++ return 0; ++ ++error: ++ bus_close_connection(b); ++ return r; ++} ++ ++static void bus_close_connection(struct bus *b) ++{ ++ if (!b) ++ return; ++ ++ /* ++ * A bus connection is closed by simply calling close() on the ++ * connection owner file descriptor. The unique name and all owned ++ * well-known names of the conneciton will disappear. ++ * See kdbus.connection(7). ++ */ ++ if (b->pool != MAP_FAILED) ++ munmap(b->pool, POOL_SIZE); ++ if (b->fd >= 0) ++ close(b->fd); ++ free(b); ++} ++ ++static void bus_poool_free_slice(struct bus *b, uint64_t offset) ++{ ++ struct kdbus_cmd_free cmd = { ++ .size = sizeof(cmd), ++ .offset = offset, ++ }; ++ int r; ++ ++ /* ++ * Once we're done with a piece of pool memory that was returned ++ * by a command, we have to call the KDBUS_CMD_FREE ioctl on it so it ++ * can be reused. The command takes an argument of type ++ * 'struct kdbus_cmd_free', in which the pool offset of the slice to ++ * free is stored. The ioctl is employed on the connection owner ++ * file descriptor. See kdbus.pool(7), ++ */ ++ r = kdbus_cmd_free(b->fd, &cmd); ++ if (r < 0) ++ err_r(r, "cannot free pool slice"); ++} ++ ++static int bus_acquire_name(struct bus *b, const char *name) ++{ ++ struct kdbus_item *item; ++ struct kdbus_cmd *cmd; ++ size_t size; ++ int r; ++ ++ /* ++ * This function acquires a well-known name on the bus through the ++ * KDBUS_CMD_NAME_ACQUIRE ioctl. This ioctl takes an argument of type ++ * 'struct kdbus_cmd', which is assembled below. See kdbus.name(7). ++ */ ++ size = sizeof(*cmd); ++ size += KDBUS_ITEM_SIZE(strlen(name) + 1); ++ ++ cmd = alloca(size); ++ memset(cmd, 0, size); ++ cmd->size = size; ++ ++ /* ++ * The command requires an item of type KDBUS_ITEM_NAME, and its ++ * content must be a valid bus name. ++ */ ++ item = cmd->items; ++ item->type = KDBUS_ITEM_NAME; ++ item->size = KDBUS_ITEM_HEADER_SIZE + strlen(name) + 1; ++ strcpy(item->str, name); ++ ++ /* ++ * Employ the command on the connection owner file descriptor. ++ */ ++ r = kdbus_cmd_name_acquire(b->fd, cmd); ++ if (r < 0) ++ return err_r(r, "cannot acquire name"); ++ ++ return 0; ++} ++ ++static int bus_install_name_loss_match(struct bus *b, const char *name) ++{ ++ struct kdbus_cmd_match *match; ++ struct kdbus_item *item; ++ size_t size; ++ int r; ++ ++ /* ++ * In order to install a match for signal messages, we have to ++ * assemble a 'struct kdbus_cmd_match' and use it along with the ++ * KDBUS_CMD_MATCH_ADD ioctl. See kdbus.match(7). ++ */ ++ size = sizeof(*match); ++ size += KDBUS_ITEM_SIZE(sizeof(item->name_change) + strlen(name) + 1); ++ ++ match = alloca(size); ++ memset(match, 0, size); ++ match->size = size; ++ ++ /* ++ * A match is comprised of many 'rules', each of which describes a ++ * mandatory detail of the message. All rules of a match must be ++ * satified in order to make a message pass. ++ */ ++ item = match->items; ++ ++ /* ++ * In this case, we're interested in notifications that inform us ++ * about a well-known name being removed from the bus. ++ */ ++ item->type = KDBUS_ITEM_NAME_REMOVE; ++ item->size = KDBUS_ITEM_HEADER_SIZE + ++ sizeof(item->name_change) + strlen(name) + 1; ++ ++ /* ++ * We could limit the match further and require a specific unique-ID ++ * to be the new or the old owner of the name. In this case, however, ++ * we don't, and allow 'any' id. ++ */ ++ item->name_change.old_id.id = KDBUS_MATCH_ID_ANY; ++ item->name_change.new_id.id = KDBUS_MATCH_ID_ANY; ++ ++ /* Copy in the well-known name we're interested in */ ++ strcpy(item->name_change.name, name); ++ ++ /* ++ * Add the match through the KDBUS_CMD_MATCH_ADD ioctl, employed on ++ * the connection owner fd. ++ */ ++ r = kdbus_cmd_match_add(b->fd, match); ++ if (r < 0) ++ return err_r(r, "cannot add match"); ++ ++ return 0; ++} ++ ++static int bus_poll(struct bus *b) ++{ ++ struct pollfd fds[1] = {}; ++ int r; ++ ++ /* ++ * A connection endpoint supports poll() and will wake-up the ++ * task with POLLIN set once a message has arrived. ++ */ ++ fds[0].fd = b->fd; ++ fds[0].events = POLLIN; ++ r = poll(fds, sizeof(fds) / sizeof(*fds), 0); ++ if (r < 0) ++ return err("cannot poll bus"); ++ return !!(fds[0].revents & POLLIN); ++} ++ ++static int bus_make(uid_t uid, const char *name) ++{ ++ struct kdbus_item *item; ++ struct kdbus_cmd *make; ++ char path[128], busname[128]; ++ size_t size; ++ int r, fd; ++ ++ /* ++ * Compute the full path to the 'control' node. 'arg_modname' may be ++ * set to a different value than 'kdbus' for development purposes. ++ * The 'control' node is the primary entry point to kdbus that must be ++ * used in order to create a bus. See kdbus(7) and kdbus.bus(7). ++ */ ++ snprintf(path, sizeof(path), "/sys/fs/%s/control", arg_modname); ++ ++ /* ++ * Compute the bus name. A valid bus name must always be prefixed with ++ * the EUID of the currently running process in order to avoid name ++ * conflicts. See kdbus.bus(7). ++ */ ++ snprintf(busname, sizeof(busname), "%lu-%s", (unsigned long)uid, name); ++ ++ fd = open(path, O_RDWR | O_CLOEXEC); ++ if (fd < 0) ++ return err("cannot open control file"); ++ ++ /* ++ * The KDBUS_CMD_BUS_MAKE ioctl takes an argument of type ++ * 'struct kdbus_cmd', and expects at least two items attached to ++ * it: one to decribe the bloom parameters to be propagated to ++ * connections of the bus, and the name of the bus that was computed ++ * above. Assemble this struct now, and fill it with values. ++ */ ++ size = sizeof(*make); ++ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_parameter)); ++ size += KDBUS_ITEM_SIZE(strlen(busname) + 1); ++ ++ make = alloca(size); ++ memset(make, 0, size); ++ make->size = size; ++ ++ /* ++ * Each item has a 'type' and 'size' field, and must be stored at an ++ * 8-byte aligned address. The KDBUS_ITEM_NEXT macro is used to advance ++ * the pointer. See kdbus.item(7) for more details. ++ */ ++ item = make->items; ++ item->type = KDBUS_ITEM_BLOOM_PARAMETER; ++ item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(item->bloom_parameter); ++ item->bloom_parameter.size = 8; ++ item->bloom_parameter.n_hash = 1; ++ ++ /* The name of the new bus is stored in the next item. */ ++ item = KDBUS_ITEM_NEXT(item); ++ item->type = KDBUS_ITEM_MAKE_NAME; ++ item->size = KDBUS_ITEM_HEADER_SIZE + strlen(busname) + 1; ++ strcpy(item->str, busname); ++ ++ /* ++ * Now create the bus via the KDBUS_CMD_BUS_MAKE ioctl and return the ++ * fd that was used back to the caller of this function. This fd is now ++ * called a 'bus owner file descriptor', and it controls the life-time ++ * of the newly created bus; once the file descriptor is closed, the ++ * bus goes away, and all connections are shut down. See kdbus.bus(7). ++ */ ++ r = kdbus_cmd_bus_make(fd, make); ++ if (r < 0) { ++ err_r(r, "cannot make bus"); ++ close(fd); ++ return r; ++ } ++ ++ return fd; ++} diff --git a/kdbus-avoid-the-use-of-struct-timespec.patch b/kdbus-avoid-the-use-of-struct-timespec.patch new file mode 100644 index 000000000..2b086fc26 --- /dev/null +++ b/kdbus-avoid-the-use-of-struct-timespec.patch @@ -0,0 +1,56 @@ +From: Arnd Bergmann <arnd@arndb.de> +Date: Fri, 10 Apr 2015 13:43:37 +0200 +Subject: [PATCH] kdbus: avoid the use of struct timespec + +I did a routine check for new users of 'timespec', which we are trying to remove +from the kernel in order to survive y2038. kdbus came up and looks particularly +trivial to clean up. + +This changes the three ktime_get_ts() variants used in kdbus to ktime_get_ns(), +which aside from removing timespec also simplifies the code and makes it +slightly more efficient by avoiding a two-way conversion. + +Signed-off-by: Arnd Bergmann <arnd@arndb.de> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + ipc/kdbus/metadata.c | 9 ++------- + ipc/kdbus/reply.c | 4 +--- + 2 files changed, 3 insertions(+), 10 deletions(-) + +diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c +index 06e0a54a276a..3adc6c2c2e76 100644 +--- a/ipc/kdbus/metadata.c ++++ b/ipc/kdbus/metadata.c +@@ -678,13 +678,8 @@ struct kdbus_meta_conn *kdbus_meta_conn_unref(struct kdbus_meta_conn *mc) + static void kdbus_meta_conn_collect_timestamp(struct kdbus_meta_conn *mc, + struct kdbus_kmsg *kmsg) + { +- struct timespec ts; +- +- ktime_get_ts(&ts); +- mc->ts.monotonic_ns = timespec_to_ns(&ts); +- +- ktime_get_real_ts(&ts); +- mc->ts.realtime_ns = timespec_to_ns(&ts); ++ mc->ts.monotonic_ns = ktime_get_ns(); ++ mc->ts.realtime_ns = ktime_get_real_ns(); + + if (kmsg) + mc->ts.seqnum = kmsg->seq; +diff --git a/ipc/kdbus/reply.c b/ipc/kdbus/reply.c +index 6b3bd81bbb4d..008dca801627 100644 +--- a/ipc/kdbus/reply.c ++++ b/ipc/kdbus/reply.c +@@ -204,11 +204,9 @@ void kdbus_reply_list_scan_work(struct work_struct *work) + container_of(work, struct kdbus_conn, work.work); + struct kdbus_reply *reply, *reply_tmp; + u64 deadline = ~0ULL; +- struct timespec64 ts; + u64 now; + +- ktime_get_ts64(&ts); +- now = timespec64_to_ns(&ts); ++ now = ktime_get_ns(); + + mutex_lock(&conn->lock); + if (!kdbus_conn_active(conn)) { diff --git a/kdbus-connection-fix-handling-of-failed-fget.patch b/kdbus-connection-fix-handling-of-failed-fget.patch new file mode 100644 index 000000000..939e17da0 --- /dev/null +++ b/kdbus-connection-fix-handling-of-failed-fget.patch @@ -0,0 +1,36 @@ +From: Daniel Mack <daniel@zonque.org> +Date: Tue, 17 Mar 2015 19:48:24 +0100 +Subject: [PATCH] kdbus: connection: fix handling of failed fget() + +The patch 5fc8dd5c84fc: "kdbus: add connection, queue handling and +message validation code" from Sep 11, 2014, leads to the following +static checker warning: + + ipc/kdbus/connection.c:2000 kdbus_cmd_send() + warn: 'cancel_fd' isn't an ERR_PTR + +Fix this by checking for NULL pointers returned from fget(). + +Reported-by: Dan Carpenter <dan.carpenter@oracle.com> +Signed-off-by: Daniel Mack <daniel@zonque.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + ipc/kdbus/connection.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c +index e554f1a71aa1..ab476fa9ccca 100644 +--- a/ipc/kdbus/connection.c ++++ b/ipc/kdbus/connection.c +@@ -1997,9 +1997,8 @@ int kdbus_cmd_send(struct kdbus_conn *conn, struct file *f, void __user *argp) + + if (argv[1].item) { + cancel_fd = fget(argv[1].item->fds[0]); +- if (IS_ERR(cancel_fd)) { +- ret = PTR_ERR(cancel_fd); +- cancel_fd = NULL; ++ if (!cancel_fd) { ++ ret = -EBADF; + goto exit; + } + diff --git a/kdbus-copy-small-ioctl-payloads-to-stack.patch b/kdbus-copy-small-ioctl-payloads-to-stack.patch new file mode 100644 index 000000000..cc049042e --- /dev/null +++ b/kdbus-copy-small-ioctl-payloads-to-stack.patch @@ -0,0 +1,167 @@ +From: David Herrmann <dh.herrmann@gmail.com> +Date: Mon, 20 Apr 2015 16:20:59 +0200 +Subject: [PATCH] kdbus: copy small ioctl payloads to stack + +Right now, we use memdup_user() on all ioctl payloads. However, most of +the time an ioctl payload is pretty small. 512 bytes on stack seem +reasonable (similar to what poll() does) to speed up small ioctl payloads. +Add a command-buffer to kdbus_args and use it instead of kmalloc() for +reasonably small payloads. + +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Acked-by: Daniel Mack <daniel@zonque.org> +--- + ipc/kdbus/handle.c | 30 ++++++++++++++++++++++++++---- + ipc/kdbus/handle.h | 5 +++++ + ipc/kdbus/util.c | 45 --------------------------------------------- + ipc/kdbus/util.h | 1 - + 4 files changed, 31 insertions(+), 50 deletions(-) + +diff --git a/ipc/kdbus/handle.c b/ipc/kdbus/handle.c +index 6230c7ef4347..07527990a051 100644 +--- a/ipc/kdbus/handle.c ++++ b/ipc/kdbus/handle.c +@@ -146,11 +146,32 @@ static int kdbus_args_negotiate(struct kdbus_args *args) + int __kdbus_args_parse(struct kdbus_args *args, void __user *argp, + size_t type_size, size_t items_offset, void **out) + { ++ u64 user_size; + int ret, i; + +- args->cmd = kdbus_memdup_user(argp, type_size, KDBUS_CMD_MAX_SIZE); +- if (IS_ERR(args->cmd)) +- return PTR_ERR(args->cmd); ++ ret = kdbus_copy_from_user(&user_size, argp, sizeof(user_size)); ++ if (ret < 0) ++ return ret; ++ ++ if (user_size < type_size) ++ return -EINVAL; ++ if (user_size > KDBUS_CMD_MAX_SIZE) ++ return -EMSGSIZE; ++ ++ if (user_size <= sizeof(args->cmd_buf)) { ++ if (copy_from_user(args->cmd_buf, argp, user_size)) ++ return -EFAULT; ++ args->cmd = (void*)args->cmd_buf; ++ } else { ++ args->cmd = memdup_user(argp, user_size); ++ if (IS_ERR(args->cmd)) ++ return PTR_ERR(args->cmd); ++ } ++ ++ if (args->cmd->size != user_size) { ++ ret = -EINVAL; ++ goto error; ++ } + + args->cmd->return_flags = 0; + args->user = argp; +@@ -207,7 +228,8 @@ int kdbus_args_clear(struct kdbus_args *args, int ret) + if (put_user(args->cmd->return_flags, + &args->user->return_flags)) + ret = -EFAULT; +- kfree(args->cmd); ++ if (args->cmd != (void*)args->cmd_buf) ++ kfree(args->cmd); + args->cmd = NULL; + } + +diff --git a/ipc/kdbus/handle.h b/ipc/kdbus/handle.h +index 93a372d554a2..13c59d975728 100644 +--- a/ipc/kdbus/handle.h ++++ b/ipc/kdbus/handle.h +@@ -45,6 +45,7 @@ struct kdbus_arg { + * @argv: array of items this command supports + * @user: set by parser to user-space location of current command + * @cmd: set by parser to kernel copy of command payload ++ * @cmd_buf: 512 bytes inline buf to avoid kmalloc() on small cmds + * @items: points to item array in @cmd + * @items_size: size of @items in bytes + * +@@ -52,6 +53,9 @@ struct kdbus_arg { + * The ioctl handler has to pre-fill the flags and allowed items before passing + * the object to kdbus_args_parse(). The parser will copy the command payload + * into kernel-space and verify the correctness of the data. ++ * ++ * We use a 512 bytes buffer for small command payloads, to be allocated on ++ * stack on syscall entrance. + */ + struct kdbus_args { + u64 allowed_flags; +@@ -60,6 +64,7 @@ struct kdbus_args { + + struct kdbus_cmd __user *user; + struct kdbus_cmd *cmd; ++ u8 cmd_buf[512]; + + struct kdbus_item *items; + size_t items_size; +diff --git a/ipc/kdbus/util.c b/ipc/kdbus/util.c +index eaa806a27997..72b188330896 100644 +--- a/ipc/kdbus/util.c ++++ b/ipc/kdbus/util.c +@@ -50,51 +50,6 @@ int kdbus_copy_from_user(void *dest, void __user *user_ptr, size_t size) + } + + /** +- * kdbus_memdup_user() - copy dynamically sized object from user-space +- * @user_ptr: user-provided source buffer +- * @sz_min: minimum object size +- * @sz_max: maximum object size +- * +- * This copies a dynamically sized object from user-space into kernel-space. We +- * require the object to have a 64bit size field at offset 0. We read it out +- * first, allocate a suitably sized buffer and then copy all data. +- * +- * The @sz_min and @sz_max parameters define possible min and max object sizes +- * so user-space cannot trigger un-bound kernel-space allocations. +- * +- * The same alignment-restrictions as described in kdbus_copy_from_user() apply. +- * +- * Return: pointer to dynamically allocated copy, or ERR_PTR() on failure. +- */ +-void *kdbus_memdup_user(void __user *user_ptr, size_t sz_min, size_t sz_max) +-{ +- void *ptr; +- u64 size; +- int ret; +- +- ret = kdbus_copy_from_user(&size, user_ptr, sizeof(size)); +- if (ret < 0) +- return ERR_PTR(ret); +- +- if (size < sz_min) +- return ERR_PTR(-EINVAL); +- +- if (size > sz_max) +- return ERR_PTR(-EMSGSIZE); +- +- ptr = memdup_user(user_ptr, size); +- if (IS_ERR(ptr)) +- return ptr; +- +- if (*(u64 *)ptr != size) { +- kfree(ptr); +- return ERR_PTR(-EINVAL); +- } +- +- return ptr; +-} +- +-/** + * kdbus_verify_uid_prefix() - verify UID prefix of a user-supplied name + * @name: user-supplied name to verify + * @user_ns: user-namespace to act in +diff --git a/ipc/kdbus/util.h b/ipc/kdbus/util.h +index 740b19880985..9fedf8ab41cd 100644 +--- a/ipc/kdbus/util.h ++++ b/ipc/kdbus/util.h +@@ -64,7 +64,6 @@ int kdbus_verify_uid_prefix(const char *name, struct user_namespace *user_ns, + int kdbus_sanitize_attach_flags(u64 flags, u64 *attach_flags); + + int kdbus_copy_from_user(void *dest, void __user *user_ptr, size_t size); +-void *kdbus_memdup_user(void __user *user_ptr, size_t sz_min, size_t sz_max); + + struct kvec; + diff --git a/kdbus-drop-kdbus_meta_attach_mask-modparam.patch b/kdbus-drop-kdbus_meta_attach_mask-modparam.patch new file mode 100644 index 000000000..476869037 --- /dev/null +++ b/kdbus-drop-kdbus_meta_attach_mask-modparam.patch @@ -0,0 +1,1071 @@ +From: David Herrmann <dh.herrmann@gmail.com> +Date: Fri, 22 May 2015 10:25:08 +0200 +Subject: [PATCH] kdbus: drop kdbus_meta_attach_mask modparam + +This parameter was introduced to mask out experimental metadata items. As +the discussion on metadata items has shifted, plans changed: Enable all +metadata items and drop the controversial items entirely. Lets not work +around differences of opinions, but figure them out. + +Also drop all the related kselftests infrastructure to make sure the tests +still run fine. + +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Acked-by: Daniel Mack <daniel@zonque.org> +--- + ipc/kdbus/main.c | 12 - + ipc/kdbus/metadata.c | 1 - + ipc/kdbus/metadata.h | 2 - + tools/testing/selftests/kdbus/Makefile | 1 - + tools/testing/selftests/kdbus/kdbus-test.c | 25 +- + tools/testing/selftests/kdbus/kdbus-test.h | 2 - + tools/testing/selftests/kdbus/test-attach-flags.c | 750 ---------------------- + tools/testing/selftests/kdbus/test-connection.c | 34 +- + 8 files changed, 13 insertions(+), 814 deletions(-) + delete mode 100644 tools/testing/selftests/kdbus/test-attach-flags.c + +diff --git a/ipc/kdbus/main.c b/ipc/kdbus/main.c +index f8eac78cace6..1ad4dc8dafa1 100644 +--- a/ipc/kdbus/main.c ++++ b/ipc/kdbus/main.c +@@ -15,7 +15,6 @@ + #include <linux/fs.h> + #include <linux/init.h> + #include <linux/module.h> +-#include <linux/moduleparam.h> + + #include "util.h" + #include "fs.h" +@@ -79,17 +78,6 @@ + /* kdbus mount-point /sys/fs/kdbus */ + static struct kobject *kdbus_dir; + +-/* global module option to apply a mask to exported metadata */ +-unsigned long long kdbus_meta_attach_mask = KDBUS_ATTACH_TIMESTAMP | +- KDBUS_ATTACH_CREDS | +- KDBUS_ATTACH_PIDS | +- KDBUS_ATTACH_AUXGROUPS | +- KDBUS_ATTACH_NAMES | +- KDBUS_ATTACH_SECLABEL | +- KDBUS_ATTACH_CONN_DESCRIPTION; +-MODULE_PARM_DESC(attach_flags_mask, "Attach-flags mask for exported metadata"); +-module_param_named(attach_flags_mask, kdbus_meta_attach_mask, ullong, 0644); +- + static int __init kdbus_init(void) + { + int ret; +diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c +index 174436f0aa01..b908b6314a00 100644 +--- a/ipc/kdbus/metadata.c ++++ b/ipc/kdbus/metadata.c +@@ -835,7 +835,6 @@ int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp, + } + + *mask &= valid; +- *mask &= kdbus_meta_attach_mask; + + if (!*mask) + goto exit; +diff --git a/ipc/kdbus/metadata.h b/ipc/kdbus/metadata.h +index 42c942b34d2c..79b6ac31c8ad 100644 +--- a/ipc/kdbus/metadata.h ++++ b/ipc/kdbus/metadata.h +@@ -24,8 +24,6 @@ struct kdbus_pool_slice; + struct kdbus_meta_proc; + struct kdbus_meta_conn; + +-extern unsigned long long kdbus_meta_attach_mask; +- + struct kdbus_meta_proc *kdbus_meta_proc_new(void); + struct kdbus_meta_proc *kdbus_meta_proc_ref(struct kdbus_meta_proc *mp); + struct kdbus_meta_proc *kdbus_meta_proc_unref(struct kdbus_meta_proc *mp); +diff --git a/tools/testing/selftests/kdbus/Makefile b/tools/testing/selftests/kdbus/Makefile +index de8242f9b00e..076f9f40566d 100644 +--- a/tools/testing/selftests/kdbus/Makefile ++++ b/tools/testing/selftests/kdbus/Makefile +@@ -11,7 +11,6 @@ OBJS= \ + kdbus-test.o \ + kdbus-test.o \ + test-activator.o \ +- test-attach-flags.o \ + test-benchmark.o \ + test-bus.o \ + test-chat.o \ +diff --git a/tools/testing/selftests/kdbus/kdbus-test.c b/tools/testing/selftests/kdbus/kdbus-test.c +index a43674ccdeb0..294e82a83ab6 100644 +--- a/tools/testing/selftests/kdbus/kdbus-test.c ++++ b/tools/testing/selftests/kdbus/kdbus-test.c +@@ -48,7 +48,6 @@ struct kdbus_test_args { + char *root; + char *test; + char *busname; +- char *mask_param_path; + }; + + static const struct kdbus_test tests[] = { +@@ -274,13 +273,6 @@ static const struct kdbus_test tests[] = { + .func = kdbus_test_benchmark_uds, + .flags = TEST_CREATE_BUS, + }, +- { +- /* Last test */ +- .name = "attach-flags", +- .desc = "attach flags mask", +- .func = kdbus_test_attach_flags, +- .flags = 0, +- }, + }; + + #define N_TESTS ((int) (sizeof(tests) / sizeof(tests[0]))) +@@ -323,7 +315,6 @@ static int test_prepare_env(const struct kdbus_test *t, + + env->root = args->root; + env->module = args->module; +- env->mask_param_path = args->mask_param_path; + + return 0; + } +@@ -754,8 +745,7 @@ int start_tests(struct kdbus_test_args *kdbus_args) + { + int ret; + bool namespaces; +- uint64_t kdbus_param_mask; +- static char fspath[4096], parampath[4096]; ++ static char fspath[4096]; + + namespaces = (kdbus_args->mntns || kdbus_args->pidns || + kdbus_args->userns); +@@ -791,19 +781,6 @@ int start_tests(struct kdbus_test_args *kdbus_args) + kdbus_args->root = fspath; + } + +- snprintf(parampath, sizeof(parampath), +- "/sys/module/%s/parameters/attach_flags_mask", +- kdbus_args->module); +- kdbus_args->mask_param_path = parampath; +- +- ret = kdbus_sysfs_get_parameter_mask(kdbus_args->mask_param_path, +- &kdbus_param_mask); +- if (ret < 0) +- return TEST_ERR; +- +- printf("# Starting tests with an attach_flags_mask=0x%llx\n", +- (unsigned long long)kdbus_param_mask); +- + /* Start tests */ + if (namespaces) + ret = run_tests_in_namespaces(kdbus_args); +diff --git a/tools/testing/selftests/kdbus/kdbus-test.h b/tools/testing/selftests/kdbus/kdbus-test.h +index 647331883763..a5c6ae81b81b 100644 +--- a/tools/testing/selftests/kdbus/kdbus-test.h ++++ b/tools/testing/selftests/kdbus/kdbus-test.h +@@ -5,7 +5,6 @@ struct kdbus_test_env { + char *buspath; + const char *root; + const char *module; +- const char *mask_param_path; + int control_fd; + struct kdbus_conn *conn; + }; +@@ -44,7 +43,6 @@ enum { + ASSERT_EXIT_VAL(cond, EXIT_FAILURE) + + int kdbus_test_activator(struct kdbus_test_env *env); +-int kdbus_test_attach_flags(struct kdbus_test_env *env); + int kdbus_test_benchmark(struct kdbus_test_env *env); + int kdbus_test_benchmark_nomemfds(struct kdbus_test_env *env); + int kdbus_test_benchmark_uds(struct kdbus_test_env *env); +diff --git a/tools/testing/selftests/kdbus/test-attach-flags.c b/tools/testing/selftests/kdbus/test-attach-flags.c +deleted file mode 100644 +index deee7c332f25..000000000000 +--- a/tools/testing/selftests/kdbus/test-attach-flags.c ++++ /dev/null +@@ -1,750 +0,0 @@ +-#include <stdio.h> +-#include <string.h> +-#include <stdlib.h> +-#include <stdbool.h> +-#include <stddef.h> +-#include <fcntl.h> +-#include <unistd.h> +-#include <stdint.h> +-#include <errno.h> +-#include <assert.h> +-#include <sys/capability.h> +-#include <sys/mman.h> +-#include <sys/stat.h> +-#include <sys/types.h> +-#include <linux/unistd.h> +- +-#include "kdbus-api.h" +-#include "kdbus-test.h" +-#include "kdbus-util.h" +-#include "kdbus-enum.h" +- +-/* +- * Should be the sum of the currently supported and compiled-in +- * KDBUS_ITEMS_* that reflect KDBUS_ATTACH_* flags. +- */ +-static unsigned int KDBUS_TEST_ITEMS_SUM = KDBUS_ATTACH_ITEMS_TYPE_SUM; +- +-static struct kdbus_conn *__kdbus_hello(const char *path, uint64_t flags, +- uint64_t attach_flags_send, +- uint64_t attach_flags_recv) +-{ +- struct kdbus_cmd_free cmd_free = {}; +- int ret, fd; +- struct kdbus_conn *conn; +- struct { +- struct kdbus_cmd_hello hello; +- +- struct { +- uint64_t size; +- uint64_t type; +- char str[16]; +- } conn_name; +- +- uint8_t extra_items[0]; +- } h; +- +- memset(&h, 0, sizeof(h)); +- +- 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_send = attach_flags_send; +- h.hello.attach_flags_recv = attach_flags_recv; +- h.conn_name.type = KDBUS_ITEM_CONN_DESCRIPTION; +- 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 = kdbus_cmd_hello(fd, (struct kdbus_cmd_hello *) &h.hello); +- if (ret < 0) { +- kdbus_printf("--- error when saying hello: %d (%m)\n", ret); +- return NULL; +- } +- +- kdbus_printf("-- New connection ID : %llu\n", +- (unsigned long long)h.hello.id); +- +- cmd_free.size = sizeof(cmd_free); +- cmd_free.offset = h.hello.offset; +- ret = kdbus_cmd_free(fd, &cmd_free); +- if (ret < 0) +- return NULL; +- +- conn = malloc(sizeof(*conn)); +- if (!conn) { +- kdbus_printf("unable to malloc()!?\n"); +- return NULL; +- } +- +- conn->buf = mmap(NULL, POOL_SIZE, PROT_READ, MAP_SHARED, fd, 0); +- if (conn->buf == MAP_FAILED) { +- ret = -errno; +- free(conn); +- close(fd); +- kdbus_printf("--- error mmap: %d (%m)\n", ret); +- return NULL; +- } +- +- conn->fd = fd; +- conn->id = h.hello.id; +- return conn; +-} +- +-static int kdbus_test_peers_creation(struct kdbus_test_env *env) +-{ +- int ret; +- int control_fd; +- char *path; +- char *busname; +- char buspath[2048]; +- char control_path[2048]; +- uint64_t attach_flags_mask; +- struct kdbus_conn *conn; +- +- snprintf(control_path, sizeof(control_path), +- "%s/control", env->root); +- +- /* +- * Set kdbus system-wide mask to 0, this has nothing +- * to do with the following tests, bus and connection +- * creation nor connection update, but we do it so we are +- * sure that everything work as expected +- */ +- +- attach_flags_mask = 0; +- ret = kdbus_sysfs_set_parameter_mask(env->mask_param_path, +- attach_flags_mask); +- ASSERT_RETURN(ret == 0); +- +- +- /* +- * Create bus with a full set of ATTACH flags +- */ +- +- control_fd = open(control_path, O_RDWR); +- ASSERT_RETURN(control_fd >= 0); +- +- busname = unique_name("test-peers-creation-bus"); +- ASSERT_RETURN(busname); +- +- ret = kdbus_create_bus(control_fd, busname, _KDBUS_ATTACH_ALL, +- 0, &path); +- ASSERT_RETURN(ret == 0); +- +- snprintf(buspath, sizeof(buspath), "%s/%s/bus", env->root, path); +- +- /* +- * Create a connection with an empty send attach flags, or +- * with just KDBUS_ATTACH_CREDS, this should fail +- */ +- conn = __kdbus_hello(buspath, 0, 0, 0); +- ASSERT_RETURN(conn == NULL); +- ASSERT_RETURN(errno == ECONNREFUSED); +- +- conn = __kdbus_hello(buspath, 0, KDBUS_ATTACH_CREDS, +- _KDBUS_ATTACH_ALL); +- ASSERT_RETURN(conn == NULL); +- ASSERT_RETURN(errno == ECONNREFUSED); +- +- conn = __kdbus_hello(buspath, 0, _KDBUS_ATTACH_ALL, 0); +- ASSERT_RETURN(conn); +- +- /* Try to cut back some send attach flags */ +- ret = kdbus_conn_update_attach_flags(conn, +- KDBUS_ATTACH_CREDS| +- KDBUS_ATTACH_PIDS, +- _KDBUS_ATTACH_ALL); +- ASSERT_RETURN(ret == -EINVAL); +- +- ret = kdbus_conn_update_attach_flags(conn, +- _KDBUS_ATTACH_ALL, 0); +- ASSERT_RETURN(ret == 0); +- +- kdbus_conn_free(conn); +- free(path); +- free(busname); +- close(control_fd); +- +- +- /* Test a new bus with KDBUS_ATTACH_PIDS */ +- +- control_fd = open(control_path, O_RDWR); +- ASSERT_RETURN(control_fd >= 0); +- +- busname = unique_name("test-peer-flags-bus"); +- ASSERT_RETURN(busname); +- +- ret = kdbus_create_bus(control_fd, busname, KDBUS_ATTACH_PIDS, +- 0, &path); +- ASSERT_RETURN(ret == 0); +- +- snprintf(buspath, sizeof(buspath), "%s/%s/bus", env->root, path); +- +- /* +- * Create a connection with an empty send attach flags, or +- * all flags except KDBUS_ATTACH_PIDS +- */ +- conn = __kdbus_hello(buspath, 0, 0, 0); +- ASSERT_RETURN(conn == NULL); +- ASSERT_RETURN(errno == ECONNREFUSED); +- +- conn = __kdbus_hello(buspath, 0, +- _KDBUS_ATTACH_ALL & ~KDBUS_ATTACH_PIDS, +- _KDBUS_ATTACH_ALL); +- ASSERT_RETURN(conn == NULL); +- ASSERT_RETURN(errno == ECONNREFUSED); +- +- /* The following should succeed */ +- conn = __kdbus_hello(buspath, 0, KDBUS_ATTACH_PIDS, 0); +- ASSERT_RETURN(conn); +- kdbus_conn_free(conn); +- +- conn = __kdbus_hello(buspath, 0, _KDBUS_ATTACH_ALL, 0); +- ASSERT_RETURN(conn); +- +- ret = kdbus_conn_update_attach_flags(conn, +- _KDBUS_ATTACH_ALL & +- ~KDBUS_ATTACH_PIDS, +- _KDBUS_ATTACH_ALL); +- ASSERT_RETURN(ret == -EINVAL); +- +- ret = kdbus_conn_update_attach_flags(conn, 0, +- _KDBUS_ATTACH_ALL); +- ASSERT_RETURN(ret == -EINVAL); +- +- /* Now we want only KDBUS_ATTACH_PIDS */ +- ret = kdbus_conn_update_attach_flags(conn, +- KDBUS_ATTACH_PIDS, 0); +- ASSERT_RETURN(ret == 0); +- +- kdbus_conn_free(conn); +- free(path); +- free(busname); +- close(control_fd); +- +- +- /* +- * Create bus with 0 as ATTACH flags, the bus does not +- * require any attach flags +- */ +- +- control_fd = open(control_path, O_RDWR); +- ASSERT_RETURN(control_fd >= 0); +- +- busname = unique_name("test-peer-flags-bus"); +- ASSERT_RETURN(busname); +- +- ret = kdbus_create_bus(control_fd, busname, 0, 0, &path); +- ASSERT_RETURN(ret == 0); +- +- snprintf(buspath, sizeof(buspath), "%s/%s/bus", env->root, path); +- +- /* Bus is open it does not require any send attach flags */ +- conn = __kdbus_hello(buspath, 0, 0, 0); +- ASSERT_RETURN(conn); +- kdbus_conn_free(conn); +- +- conn = __kdbus_hello(buspath, 0, _KDBUS_ATTACH_ALL, 0); +- ASSERT_RETURN(conn); +- +- ret = kdbus_conn_update_attach_flags(conn, 0, 0); +- ASSERT_RETURN(ret == 0); +- +- ret = kdbus_conn_update_attach_flags(conn, KDBUS_ATTACH_CREDS, 0); +- ASSERT_RETURN(ret == 0); +- +- kdbus_conn_free(conn); +- free(path); +- free(busname); +- close(control_fd); +- +- return 0; +-} +- +-static int kdbus_test_peers_info(struct kdbus_test_env *env) +-{ +- int ret; +- int control_fd; +- char *path; +- char *busname; +- unsigned int i = 0; +- uint64_t offset = 0; +- char buspath[2048]; +- char control_path[2048]; +- uint64_t attach_flags_mask; +- struct kdbus_item *item; +- struct kdbus_info *info; +- struct kdbus_conn *conn; +- struct kdbus_conn *reader; +- unsigned long long attach_count = 0; +- +- snprintf(control_path, sizeof(control_path), +- "%s/control", env->root); +- +- attach_flags_mask = 0; +- ret = kdbus_sysfs_set_parameter_mask(env->mask_param_path, +- attach_flags_mask); +- ASSERT_RETURN(ret == 0); +- +- control_fd = open(control_path, O_RDWR); +- ASSERT_RETURN(control_fd >= 0); +- +- busname = unique_name("test-peers-info-bus"); +- ASSERT_RETURN(busname); +- +- ret = kdbus_create_bus(control_fd, busname, _KDBUS_ATTACH_ALL, +- 0, &path); +- ASSERT_RETURN(ret == 0); +- +- snprintf(buspath, sizeof(buspath), "%s/%s/bus", env->root, path); +- +- /* Create connections with the appropriate flags */ +- conn = __kdbus_hello(buspath, 0, _KDBUS_ATTACH_ALL, 0); +- ASSERT_RETURN(conn); +- +- reader = __kdbus_hello(buspath, 0, _KDBUS_ATTACH_ALL, 0); +- ASSERT_RETURN(reader); +- +- ret = kdbus_conn_info(reader, conn->id, NULL, +- _KDBUS_ATTACH_ALL, &offset); +- ASSERT_RETURN(ret == 0); +- +- info = (struct kdbus_info *)(reader->buf + offset); +- ASSERT_RETURN(info->id == conn->id); +- +- /* all attach flags are masked, no metadata */ +- KDBUS_ITEM_FOREACH(item, info, items) +- i++; +- +- ASSERT_RETURN(i == 0); +- +- kdbus_free(reader, offset); +- +- /* Set the mask to _KDBUS_ATTACH_ANY */ +- attach_flags_mask = _KDBUS_ATTACH_ANY; +- ret = kdbus_sysfs_set_parameter_mask(env->mask_param_path, +- attach_flags_mask); +- ASSERT_RETURN(ret == 0); +- +- ret = kdbus_conn_info(reader, conn->id, NULL, +- _KDBUS_ATTACH_ALL, &offset); +- ASSERT_RETURN(ret == 0); +- +- info = (struct kdbus_info *)(reader->buf + offset); +- ASSERT_RETURN(info->id == conn->id); +- +- attach_count = 0; +- KDBUS_ITEM_FOREACH(item, info, items) +- attach_count += item->type; +- +- /* +- * All flags have been returned except for: +- * KDBUS_ITEM_TIMESTAMP and +- * KDBUS_ITEM_OWNED_NAME we do not own any name. +- */ +- ASSERT_RETURN(attach_count == (KDBUS_TEST_ITEMS_SUM - +- KDBUS_ITEM_OWNED_NAME - +- KDBUS_ITEM_TIMESTAMP)); +- +- kdbus_free(reader, offset); +- +- /* Request only OWNED names */ +- ret = kdbus_conn_info(reader, conn->id, NULL, +- KDBUS_ATTACH_NAMES, &offset); +- ASSERT_RETURN(ret == 0); +- +- info = (struct kdbus_info *)(reader->buf + offset); +- ASSERT_RETURN(info->id == conn->id); +- +- attach_count = 0; +- KDBUS_ITEM_FOREACH(item, info, items) +- attach_count += item->type; +- +- /* we should not get any metadata since we do not own names */ +- ASSERT_RETURN(attach_count == 0); +- +- kdbus_free(reader, offset); +- +- kdbus_conn_free(conn); +- kdbus_conn_free(reader); +- +- return 0; +-} +- +-/** +- * @kdbus_mask_param: kdbus module mask parameter (system-wide) +- * @requested_meta: The bus owner metadata that we want +- * @expected_items: The returned KDBUS_ITEMS_* sum. Used to +- * validate the returned metadata items +- */ +-static int kdbus_cmp_bus_creator_metadata(struct kdbus_test_env *env, +- struct kdbus_conn *conn, +- uint64_t kdbus_mask_param, +- uint64_t requested_meta, +- unsigned long expected_items) +-{ +- int ret; +- uint64_t offset = 0; +- struct kdbus_info *info; +- struct kdbus_item *item; +- unsigned long attach_count = 0; +- +- ret = kdbus_sysfs_set_parameter_mask(env->mask_param_path, +- kdbus_mask_param); +- ASSERT_RETURN(ret == 0); +- +- ret = kdbus_bus_creator_info(conn, requested_meta, &offset); +- ASSERT_RETURN(ret == 0); +- +- info = (struct kdbus_info *)(conn->buf + offset); +- +- KDBUS_ITEM_FOREACH(item, info, items) +- attach_count += item->type; +- +- ASSERT_RETURN(attach_count == expected_items); +- +- ret = kdbus_free(conn, offset); +- ASSERT_RETURN(ret == 0); +- +- return 0; +-} +- +-static int kdbus_test_bus_creator_info(struct kdbus_test_env *env) +-{ +- int ret; +- int control_fd; +- char *path; +- char *busname; +- char buspath[2048]; +- char control_path[2048]; +- uint64_t attach_flags_mask; +- struct kdbus_conn *conn; +- unsigned long expected_items = 0; +- +- snprintf(control_path, sizeof(control_path), +- "%s/control", env->root); +- +- control_fd = open(control_path, O_RDWR); +- ASSERT_RETURN(control_fd >= 0); +- +- busname = unique_name("test-peers-info-bus"); +- ASSERT_RETURN(busname); +- +- /* +- * Now the bus allows us to see all its KDBUS_ATTACH_* +- * items +- */ +- ret = kdbus_create_bus(control_fd, busname, 0, +- _KDBUS_ATTACH_ALL, &path); +- ASSERT_RETURN(ret == 0); +- +- snprintf(buspath, sizeof(buspath), "%s/%s/bus", env->root, path); +- +- conn = __kdbus_hello(buspath, 0, 0, 0); +- ASSERT_RETURN(conn); +- +- /* +- * Start with a kdbus module mask set to _KDBUS_ATTACH_ANY +- */ +- attach_flags_mask = _KDBUS_ATTACH_ANY; +- +- /* +- * All flags will be returned except for: +- * KDBUS_ITEM_TIMESTAMP +- * KDBUS_ITEM_OWNED_NAME +- * KDBUS_ITEM_CONN_DESCRIPTION +- * +- * An extra flags is always returned KDBUS_ITEM_MAKE_NAME +- * which contains the bus name +- */ +- expected_items = KDBUS_TEST_ITEMS_SUM + KDBUS_ITEM_MAKE_NAME; +- expected_items -= KDBUS_ITEM_TIMESTAMP + +- KDBUS_ITEM_OWNED_NAME + +- KDBUS_ITEM_CONN_DESCRIPTION; +- ret = kdbus_cmp_bus_creator_metadata(env, conn, +- attach_flags_mask, +- _KDBUS_ATTACH_ALL, +- expected_items); +- ASSERT_RETURN(ret == 0); +- +- /* +- * We should have: +- * KDBUS_ITEM_PIDS + KDBUS_ITEM_CREDS + KDBUS_ITEM_MAKE_NAME +- */ +- expected_items = KDBUS_ITEM_PIDS + KDBUS_ITEM_CREDS + +- KDBUS_ITEM_MAKE_NAME; +- ret = kdbus_cmp_bus_creator_metadata(env, conn, +- attach_flags_mask, +- KDBUS_ATTACH_PIDS | +- KDBUS_ATTACH_CREDS, +- expected_items); +- ASSERT_RETURN(ret == 0); +- +- /* KDBUS_ITEM_MAKE_NAME is always returned */ +- expected_items = KDBUS_ITEM_MAKE_NAME; +- ret = kdbus_cmp_bus_creator_metadata(env, conn, +- attach_flags_mask, +- 0, expected_items); +- ASSERT_RETURN(ret == 0); +- +- /* +- * Restrict kdbus system-wide mask to KDBUS_ATTACH_PIDS +- */ +- +- attach_flags_mask = KDBUS_ATTACH_PIDS; +- +- /* +- * We should have: +- * KDBUS_ITEM_PIDS + KDBUS_ITEM_MAKE_NAME +- */ +- expected_items = KDBUS_ITEM_PIDS + KDBUS_ITEM_MAKE_NAME; +- ret = kdbus_cmp_bus_creator_metadata(env, conn, +- attach_flags_mask, +- _KDBUS_ATTACH_ALL, +- expected_items); +- ASSERT_RETURN(ret == 0); +- +- +- /* system-wide mask to 0 */ +- attach_flags_mask = 0; +- +- /* we should only see: KDBUS_ITEM_MAKE_NAME */ +- expected_items = KDBUS_ITEM_MAKE_NAME; +- ret = kdbus_cmp_bus_creator_metadata(env, conn, +- attach_flags_mask, +- _KDBUS_ATTACH_ALL, +- expected_items); +- ASSERT_RETURN(ret == 0); +- +- kdbus_conn_free(conn); +- free(path); +- free(busname); +- close(control_fd); +- +- +- /* +- * A new bus that hides all its owner metadata +- */ +- +- control_fd = open(control_path, O_RDWR); +- ASSERT_RETURN(control_fd >= 0); +- +- busname = unique_name("test-peers-info-bus"); +- ASSERT_RETURN(busname); +- +- ret = kdbus_create_bus(control_fd, busname, 0, 0, &path); +- ASSERT_RETURN(ret == 0); +- +- snprintf(buspath, sizeof(buspath), "%s/%s/bus", env->root, path); +- +- conn = __kdbus_hello(buspath, 0, 0, 0); +- ASSERT_RETURN(conn); +- +- /* +- * Start with a kdbus module mask set to _KDBUS_ATTACH_ANY +- */ +- attach_flags_mask = _KDBUS_ATTACH_ANY; +- +- /* +- * We only get the KDBUS_ITEM_MAKE_NAME +- */ +- expected_items = KDBUS_ITEM_MAKE_NAME; +- ret = kdbus_cmp_bus_creator_metadata(env, conn, +- attach_flags_mask, +- _KDBUS_ATTACH_ALL, +- expected_items); +- ASSERT_RETURN(ret == 0); +- +- /* +- * We still get only kdbus_ITEM_MAKE_NAME +- */ +- attach_flags_mask = 0; +- expected_items = KDBUS_ITEM_MAKE_NAME; +- ret = kdbus_cmp_bus_creator_metadata(env, conn, +- attach_flags_mask, +- _KDBUS_ATTACH_ALL, +- expected_items); +- ASSERT_RETURN(ret == 0); +- +- kdbus_conn_free(conn); +- free(path); +- free(busname); +- close(control_fd); +- +- +- /* +- * A new bus that shows only the PID and CREDS metadata +- * of the bus owner. +- */ +- control_fd = open(control_path, O_RDWR); +- ASSERT_RETURN(control_fd >= 0); +- +- busname = unique_name("test-peers-info-bus"); +- ASSERT_RETURN(busname); +- +- ret = kdbus_create_bus(control_fd, busname, 0, +- KDBUS_ATTACH_PIDS| +- KDBUS_ATTACH_CREDS, &path); +- ASSERT_RETURN(ret == 0); +- +- snprintf(buspath, sizeof(buspath), "%s/%s/bus", env->root, path); +- +- conn = __kdbus_hello(buspath, 0, 0, 0); +- ASSERT_RETURN(conn); +- +- /* +- * Start with a kdbus module mask set to _KDBUS_ATTACH_ANY +- */ +- attach_flags_mask = _KDBUS_ATTACH_ANY; +- +- /* +- * We should have: +- * KDBUS_ITEM_PIDS + KDBUS_ITEM_CREDS + KDBUS_ITEM_MAKE_NAME +- */ +- expected_items = KDBUS_ITEM_PIDS + KDBUS_ITEM_CREDS + +- KDBUS_ITEM_MAKE_NAME; +- ret = kdbus_cmp_bus_creator_metadata(env, conn, +- attach_flags_mask, +- _KDBUS_ATTACH_ALL, +- expected_items); +- ASSERT_RETURN(ret == 0); +- +- expected_items = KDBUS_ITEM_CREDS + KDBUS_ITEM_MAKE_NAME; +- ret = kdbus_cmp_bus_creator_metadata(env, conn, +- attach_flags_mask, +- KDBUS_ATTACH_CREDS, +- expected_items); +- ASSERT_RETURN(ret == 0); +- +- /* KDBUS_ITEM_MAKE_NAME is always returned */ +- expected_items = KDBUS_ITEM_MAKE_NAME; +- ret = kdbus_cmp_bus_creator_metadata(env, conn, +- attach_flags_mask, +- 0, expected_items); +- ASSERT_RETURN(ret == 0); +- +- /* +- * Restrict kdbus system-wide mask to KDBUS_ATTACH_PIDS +- */ +- +- attach_flags_mask = KDBUS_ATTACH_PIDS; +- /* +- * We should have: +- * KDBUS_ITEM_PIDS + KDBUS_ITEM_MAKE_NAME +- */ +- expected_items = KDBUS_ITEM_PIDS + KDBUS_ITEM_MAKE_NAME; +- ret = kdbus_cmp_bus_creator_metadata(env, conn, +- attach_flags_mask, +- _KDBUS_ATTACH_ALL, +- expected_items); +- ASSERT_RETURN(ret == 0); +- +- /* No KDBUS_ATTACH_CREDS */ +- expected_items = KDBUS_ITEM_MAKE_NAME; +- ret = kdbus_cmp_bus_creator_metadata(env, conn, +- attach_flags_mask, +- KDBUS_ATTACH_CREDS, +- expected_items); +- ASSERT_RETURN(ret == 0); +- +- /* system-wide mask to 0 */ +- attach_flags_mask = 0; +- +- /* we should only see: KDBUS_ITEM_MAKE_NAME */ +- expected_items = KDBUS_ITEM_MAKE_NAME; +- ret = kdbus_cmp_bus_creator_metadata(env, conn, +- attach_flags_mask, +- _KDBUS_ATTACH_ALL, +- expected_items); +- ASSERT_RETURN(ret == 0); +- +- +- kdbus_conn_free(conn); +- free(path); +- free(busname); +- close(control_fd); +- +- return 0; +-} +- +-int kdbus_test_attach_flags(struct kdbus_test_env *env) +-{ +- int ret; +- uint64_t flags_mask; +- uint64_t old_kdbus_flags_mask; +- +- /* We need CAP_DAC_OVERRIDE to overwrite the kdbus mask */ +- ret = test_is_capable(CAP_DAC_OVERRIDE, -1); +- ASSERT_RETURN(ret >= 0); +- +- /* no enough privileges, SKIP test */ +- if (!ret) +- return TEST_SKIP; +- +- /* +- * We need to be able to write to +- * "/sys/module/kdbus/parameters/attach_flags_mask" +- * perhaps we are unprvileged/privileged in its userns +- */ +- ret = access(env->mask_param_path, W_OK); +- if (ret < 0) { +- kdbus_printf("--- access() '%s' failed: %d (%m)\n", +- env->mask_param_path, -errno); +- return TEST_SKIP; +- } +- +- ret = kdbus_sysfs_get_parameter_mask(env->mask_param_path, +- &old_kdbus_flags_mask); +- ASSERT_RETURN(ret == 0); +- +- /* setup the right KDBUS_TEST_ITEMS_SUM */ +- if (!config_auditsyscall_is_enabled()) +- KDBUS_TEST_ITEMS_SUM -= KDBUS_ITEM_AUDIT; +- +- if (!config_cgroups_is_enabled()) +- KDBUS_TEST_ITEMS_SUM -= KDBUS_ITEM_CGROUP; +- +- if (!config_security_is_enabled()) +- KDBUS_TEST_ITEMS_SUM -= KDBUS_ITEM_SECLABEL; +- +- /* +- * Test the connection creation attach flags +- */ +- ret = kdbus_test_peers_creation(env); +- /* Restore previous kdbus mask */ +- kdbus_sysfs_set_parameter_mask(env->mask_param_path, +- old_kdbus_flags_mask); +- ASSERT_RETURN(ret == 0); +- +- /* +- * Test the CONN_INFO attach flags +- */ +- ret = kdbus_test_peers_info(env); +- /* Restore previous kdbus mask */ +- kdbus_sysfs_set_parameter_mask(env->mask_param_path, +- old_kdbus_flags_mask); +- ASSERT_RETURN(ret == 0); +- +- /* +- * Test the Bus creator info and its attach flags +- */ +- ret = kdbus_test_bus_creator_info(env); +- /* Restore previous kdbus mask */ +- kdbus_sysfs_set_parameter_mask(env->mask_param_path, +- old_kdbus_flags_mask); +- ASSERT_RETURN(ret == 0); +- +- ret = kdbus_sysfs_get_parameter_mask(env->mask_param_path, +- &flags_mask); +- ASSERT_RETURN(ret == 0 && old_kdbus_flags_mask == flags_mask); +- +- return TEST_OK; +-} +diff --git a/tools/testing/selftests/kdbus/test-connection.c b/tools/testing/selftests/kdbus/test-connection.c +index 5c2bf3511daa..e7c486621b04 100644 +--- a/tools/testing/selftests/kdbus/test-connection.c ++++ b/tools/testing/selftests/kdbus/test-connection.c +@@ -185,13 +185,10 @@ static int kdbus_fuzz_conn_info(struct kdbus_test_env *env, int capable) + int ret; + unsigned int cnt = 0; + uint64_t offset = 0; +- uint64_t kdbus_flags_mask; + struct kdbus_info *info; + struct kdbus_conn *conn; + struct kdbus_conn *privileged; + const struct kdbus_item *item; +- uint64_t valid_flags_set; +- uint64_t invalid_flags_set; + uint64_t valid_flags = KDBUS_ATTACH_NAMES | + KDBUS_ATTACH_CREDS | + KDBUS_ATTACH_PIDS | +@@ -227,13 +224,6 @@ static int kdbus_fuzz_conn_info(struct kdbus_test_env *env, int capable) + .ppid = getppid(), + }; + +- ret = kdbus_sysfs_get_parameter_mask(env->mask_param_path, +- &kdbus_flags_mask); +- ASSERT_RETURN(ret == 0); +- +- valid_flags_set = valid_flags & kdbus_flags_mask; +- invalid_flags_set = invalid_flags & kdbus_flags_mask; +- + ret = kdbus_conn_info(env->conn, env->conn->id, NULL, + valid_flags, &offset); + ASSERT_RETURN(ret == 0); +@@ -246,7 +236,7 @@ static int kdbus_fuzz_conn_info(struct kdbus_test_env *env, int capable) + ASSERT_RETURN(item == NULL); + + item = kdbus_get_item(info, KDBUS_ITEM_CONN_DESCRIPTION); +- if (valid_flags_set & KDBUS_ATTACH_CONN_DESCRIPTION) { ++ if (valid_flags & KDBUS_ATTACH_CONN_DESCRIPTION) { + ASSERT_RETURN(item); + } else { + ASSERT_RETURN(item == NULL); +@@ -271,7 +261,7 @@ static int kdbus_fuzz_conn_info(struct kdbus_test_env *env, int capable) + ASSERT_RETURN(item == NULL); + + cnt = kdbus_count_item(info, KDBUS_ITEM_CREDS); +- if (valid_flags_set & KDBUS_ATTACH_CREDS) { ++ if (valid_flags & KDBUS_ATTACH_CREDS) { + ASSERT_RETURN(cnt == 1); + + item = kdbus_get_item(info, KDBUS_ITEM_CREDS); +@@ -285,7 +275,7 @@ static int kdbus_fuzz_conn_info(struct kdbus_test_env *env, int capable) + } + + item = kdbus_get_item(info, KDBUS_ITEM_PIDS); +- if (valid_flags_set & KDBUS_ATTACH_PIDS) { ++ if (valid_flags & KDBUS_ATTACH_PIDS) { + ASSERT_RETURN(item); + + /* Compare item->pids with cached PIDs */ +@@ -312,7 +302,7 @@ static int kdbus_fuzz_conn_info(struct kdbus_test_env *env, int capable) + ASSERT_RETURN(info->id == conn->id); + + item = kdbus_get_item(info, KDBUS_ITEM_OWNED_NAME); +- if (valid_flags_set & KDBUS_ATTACH_NAMES) { ++ if (valid_flags & KDBUS_ATTACH_NAMES) { + ASSERT_RETURN(item && !strcmp(item->name.name, "com.example.a")); + } else { + ASSERT_RETURN(item == NULL); +@@ -340,14 +330,14 @@ static int kdbus_fuzz_conn_info(struct kdbus_test_env *env, int capable) + info = (struct kdbus_info *)(conn->buf + offset); + ASSERT_EXIT(info->id == conn->id); + +- if (valid_flags_set & KDBUS_ATTACH_NAMES) { ++ if (valid_flags & KDBUS_ATTACH_NAMES) { + item = kdbus_get_item(info, KDBUS_ITEM_OWNED_NAME); + ASSERT_EXIT(item && + strcmp(item->name.name, + "com.example.a") == 0); + } + +- if (valid_flags_set & KDBUS_ATTACH_CREDS) { ++ if (valid_flags & KDBUS_ATTACH_CREDS) { + item = kdbus_get_item(info, KDBUS_ITEM_CREDS); + ASSERT_EXIT(item); + +@@ -356,7 +346,7 @@ static int kdbus_fuzz_conn_info(struct kdbus_test_env *env, int capable) + sizeof(struct kdbus_creds)) == 0); + } + +- if (valid_flags_set & KDBUS_ATTACH_PIDS) { ++ if (valid_flags & KDBUS_ATTACH_PIDS) { + item = kdbus_get_item(info, KDBUS_ITEM_PIDS); + ASSERT_EXIT(item); + +@@ -385,7 +375,7 @@ static int kdbus_fuzz_conn_info(struct kdbus_test_env *env, int capable) + * it points to the cached creds. + */ + cnt = kdbus_count_item(info, KDBUS_ITEM_CREDS); +- if (invalid_flags_set & KDBUS_ATTACH_CREDS) { ++ if (invalid_flags & KDBUS_ATTACH_CREDS) { + ASSERT_EXIT(cnt == 1); + + item = kdbus_get_item(info, KDBUS_ITEM_CREDS); +@@ -398,7 +388,7 @@ static int kdbus_fuzz_conn_info(struct kdbus_test_env *env, int capable) + ASSERT_EXIT(cnt == 0); + } + +- if (invalid_flags_set & KDBUS_ATTACH_PIDS) { ++ if (invalid_flags & KDBUS_ATTACH_PIDS) { + cnt = kdbus_count_item(info, KDBUS_ITEM_PIDS); + ASSERT_EXIT(cnt == 1); + +@@ -411,14 +401,14 @@ static int kdbus_fuzz_conn_info(struct kdbus_test_env *env, int capable) + } + + cnt = kdbus_count_item(info, KDBUS_ITEM_CGROUP); +- if (invalid_flags_set & KDBUS_ATTACH_CGROUP) { ++ if (invalid_flags & KDBUS_ATTACH_CGROUP) { + ASSERT_EXIT(cnt == 1); + } else { + ASSERT_EXIT(cnt == 0); + } + + cnt = kdbus_count_item(info, KDBUS_ITEM_CAPS); +- if (invalid_flags_set & KDBUS_ATTACH_CAPS) { ++ if (invalid_flags & KDBUS_ATTACH_CAPS) { + ASSERT_EXIT(cnt == 1); + } else { + ASSERT_EXIT(cnt == 0); +@@ -442,7 +432,7 @@ continue_test: + ASSERT_RETURN(info->id == conn->id); + + cnt = kdbus_count_item(info, KDBUS_ITEM_OWNED_NAME); +- if (valid_flags_set & KDBUS_ATTACH_NAMES) { ++ if (valid_flags & KDBUS_ATTACH_NAMES) { + ASSERT_RETURN(cnt == 2); + } else { + ASSERT_RETURN(cnt == 0); diff --git a/kdbus-drop-obsolete-WARN_ON.patch b/kdbus-drop-obsolete-WARN_ON.patch new file mode 100644 index 000000000..5ec0253c8 --- /dev/null +++ b/kdbus-drop-obsolete-WARN_ON.patch @@ -0,0 +1,27 @@ +From: David Herrmann <dh.herrmann@gmail.com> +Date: Wed, 22 Apr 2015 19:31:50 +0200 +Subject: [PATCH] kdbus: drop obsolete WARN_ON + +entry->user is never set to an error-code. Drop the obsolete WARN_ON which +is a leftover from before the quota rework. + +Reported-by: Dan Carpenter <dan.carpenter@oracle.com> +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Acked-by: Daniel Mack <daniel@zonque.org> +--- + ipc/kdbus/queue.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipc/kdbus/queue.c b/ipc/kdbus/queue.c +index a449464a3975..25bb3ad66b98 100644 +--- a/ipc/kdbus/queue.c ++++ b/ipc/kdbus/queue.c +@@ -637,7 +637,7 @@ int kdbus_queue_entry_move(struct kdbus_queue_entry *e, + lockdep_assert_held(&src->lock); + lockdep_assert_held(&dst->lock); + +- if (WARN_ON(IS_ERR(e->user)) || WARN_ON(list_empty(&e->entry))) ++ if (WARN_ON(list_empty(&e->entry))) + return -EINVAL; + if (src == dst) + return 0; diff --git a/kdbus-drop-useless-goto.patch b/kdbus-drop-useless-goto.patch new file mode 100644 index 000000000..eeaa933c2 --- /dev/null +++ b/kdbus-drop-useless-goto.patch @@ -0,0 +1,24 @@ +From: Marc-Antoine Perennou <Marc-Antoine@Perennou.com> +Date: Fri, 5 Jun 2015 14:37:34 +0200 +Subject: [PATCH] kdbus: drop useless goto + +Signed-off-by: Marc-Antoine Perennou <Marc-Antoine@Perennou.com> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + ipc/kdbus/names.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/ipc/kdbus/names.c b/ipc/kdbus/names.c +index 5f5d84ea0e8e..d77ee08afeda 100644 +--- a/ipc/kdbus/names.c ++++ b/ipc/kdbus/names.c +@@ -514,8 +514,6 @@ int kdbus_cmd_name_acquire(struct kdbus_conn *conn, void __user *argp) + + ret = kdbus_name_acquire(conn->ep->bus->name_registry, conn, item_name, + cmd->flags, &cmd->return_flags); +- if (ret < 0) +- goto exit_dec; + + exit_dec: + atomic_dec(&conn->name_count); diff --git a/kdbus-fix-header-guard-name.patch b/kdbus-fix-header-guard-name.patch new file mode 100644 index 000000000..eea2afaa7 --- /dev/null +++ b/kdbus-fix-header-guard-name.patch @@ -0,0 +1,36 @@ +From: Lucas De Marchi <lucas.demarchi@intel.com> +Date: Tue, 17 Mar 2015 09:21:42 -0300 +Subject: [PATCH] kdbus: fix header guard name + +UAPI headers have a _UAPI_ as prefix, which is removed during +headers_install. If it's put as a suffix it will not be removed and will +be the only header with UAPI in the header guard macro. + +Signed-off-by: Lucas De Marchi <lucas.demarchi@intel.com> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + include/uapi/linux/kdbus.h | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/include/uapi/linux/kdbus.h b/include/uapi/linux/kdbus.h +index fc1d77dd7c93..2fe0a1c5056c 100644 +--- a/include/uapi/linux/kdbus.h ++++ b/include/uapi/linux/kdbus.h +@@ -5,8 +5,8 @@ + * your option) any later version. + */ + +-#ifndef _KDBUS_UAPI_H_ +-#define _KDBUS_UAPI_H_ ++#ifndef _UAPI_KDBUS_H_ ++#define _UAPI_KDBUS_H_ + + #include <linux/ioctl.h> + #include <linux/types.h> +@@ -976,4 +976,4 @@ enum kdbus_ioctl_type { + struct kdbus_cmd_match), + }; + +-#endif /* _KDBUS_UAPI_H_ */ ++#endif /* _UAPI_KDBUS_H_ */ diff --git a/kdbus-fix-minor-typo-in-the-walk-through-example.patch b/kdbus-fix-minor-typo-in-the-walk-through-example.patch new file mode 100644 index 000000000..35c623703 --- /dev/null +++ b/kdbus-fix-minor-typo-in-the-walk-through-example.patch @@ -0,0 +1,27 @@ +From: Nicolas Iooss <nicolas.iooss_linux@m4x.org> +Date: Sun, 15 Mar 2015 13:13:08 +0800 +Subject: [PATCH] kdbus: fix minor typo in the walk-through example + +s/receveiver/receiver/ + +Signed-off-by: Nicolas Iooss <nicolas.iooss_linux@m4x.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + samples/kdbus/kdbus-workers.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/samples/kdbus/kdbus-workers.c b/samples/kdbus/kdbus-workers.c +index d1d8f7a7697b..d331e0186899 100644 +--- a/samples/kdbus/kdbus-workers.c ++++ b/samples/kdbus/kdbus-workers.c +@@ -787,8 +787,8 @@ static int child_run(struct child *c) + * The 2nd item contains a vector to memory we want to send. It + * can be content of any type. In our case, we're sending a one-byte + * string only. The memory referenced by this item will be copied into +- * the pool of the receveiver connection, and does not need to be +- * valid after the command is employed. ++ * the pool of the receiver connection, and does not need to be valid ++ * after the command is employed. + */ + item = KDBUS_ITEM_NEXT(item); + item->type = KDBUS_ITEM_PAYLOAD_VEC; diff --git a/kdbus-fix-operator-precedence-issues-in-item-macros.patch b/kdbus-fix-operator-precedence-issues-in-item-macros.patch new file mode 100644 index 000000000..a030e4ca4 --- /dev/null +++ b/kdbus-fix-operator-precedence-issues-in-item-macros.patch @@ -0,0 +1,47 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Tue, 9 Jun 2015 23:59:59 +0300 +Subject: [PATCH] kdbus: fix operator precedence issues in item macros + +`_i' argument in KDBUS_ITEM_NEXT and KDBUS_ITEMS_END macros is not +enclosed into parentheses when the cast operator is applied, which +leads to improper type conversion if `_i' is supplied as a complex +expression, e.g. + + KDBUS_ITEM_NEXT(condition ? a : b) + +KDBUS_ITEMS_SIZE macro has similar issue, missing parentheses around +`_h' when using indirection operator. + +Use parentheses properly to guarantee right precedence. + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + ipc/kdbus/item.h | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/ipc/kdbus/item.h b/ipc/kdbus/item.h +index eeefd8beac3b..32909e2e7954 100644 +--- a/ipc/kdbus/item.h ++++ b/ipc/kdbus/item.h +@@ -21,8 +21,8 @@ + #include "util.h" + + /* generic access and iterators over a stream of items */ +-#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_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_ITEM_HEADER_SIZE offsetof(struct kdbus_item, data) + #define KDBUS_ITEM_SIZE(_s) KDBUS_ALIGN8(KDBUS_ITEM_HEADER_SIZE + (_s)) + #define KDBUS_ITEM_PAYLOAD_SIZE(_i) ((_i)->size - KDBUS_ITEM_HEADER_SIZE) +@@ -40,7 +40,7 @@ + (u8 *)(_i) >= (u8 *)(_is)) + + #define KDBUS_ITEMS_END(_i, _is, _s) \ +- ((u8 *)_i == ((u8 *)(_is) + KDBUS_ALIGN8(_s))) ++ ((u8 *)(_i) == ((u8 *)(_is) + KDBUS_ALIGN8(_s))) + + /** + * struct kdbus_item_header - Describes the fix part of an item diff --git a/kdbus-fix-typo.patch b/kdbus-fix-typo.patch new file mode 100644 index 000000000..2835accaf --- /dev/null +++ b/kdbus-fix-typo.patch @@ -0,0 +1,25 @@ +From: David Herrmann <dh.herrmann@gmail.com> +Date: Tue, 26 May 2015 09:29:52 +0200 +Subject: [PATCH] kdbus: fix typo + +Fix "there" -> "their" typo. + +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Acked-by: Daniel Mack <daniel@zonque.org> +--- + ipc/kdbus/connection.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c +index ab476fa9ccca..fb2c6c67c4c1 100644 +--- a/ipc/kdbus/connection.c ++++ b/ipc/kdbus/connection.c +@@ -753,7 +753,7 @@ void kdbus_conn_quota_dec(struct kdbus_conn *c, struct kdbus_user *u, + * + * kdbus is reliable. That means, we try hard to never lose messages. However, + * memory is limited, so we cannot rely on transmissions to never fail. +- * Therefore, we use quota-limits to let callers know if there unicast message ++ * Therefore, we use quota-limits to let callers know if their unicast message + * cannot be transmitted to a peer. This works fine for unicasts, but for + * broadcasts we cannot make the caller handle the transmission failure. + * Instead, we must let the destination know that it couldn't receive a diff --git a/kdbus-fix-up-documentation-of-ioctl-handlers.patch b/kdbus-fix-up-documentation-of-ioctl-handlers.patch new file mode 100644 index 000000000..47dc59224 --- /dev/null +++ b/kdbus-fix-up-documentation-of-ioctl-handlers.patch @@ -0,0 +1,185 @@ +From: David Herrmann <dh.herrmann@gmail.com> +Date: Thu, 23 Apr 2015 10:23:38 +0200 +Subject: [PATCH] kdbus: fix up documentation of ioctl handlers + +We support feature negotiation on ioctls. As this is not necessarily fully +generic, we indicate this by returning >0 from kdbus_args_parse(). +Therefore, all ioctl handlers that forward the return value of +kdbus_args_parse() might also return >0 on negotiation. Which is totally +fine and handled in kdbus_handle_ioctl(). However, the documentation of +the ioctl handlers doesn't reflect that behavior. Fix those up! + +Reported-by: Dan Carpenter <dan.carpenter@oracle.com> +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +--- + ipc/kdbus/bus.c | 4 ++-- + ipc/kdbus/connection.c | 14 +++++++------- + ipc/kdbus/endpoint.c | 4 ++-- + ipc/kdbus/match.c | 4 ++-- + ipc/kdbus/names.c | 6 +++--- + 5 files changed, 16 insertions(+), 16 deletions(-) + +diff --git a/ipc/kdbus/bus.c b/ipc/kdbus/bus.c +index d5475961b896..bbdf0f2f391e 100644 +--- a/ipc/kdbus/bus.c ++++ b/ipc/kdbus/bus.c +@@ -365,7 +365,7 @@ void kdbus_bus_eavesdrop(struct kdbus_bus *bus, + * @domain: domain to operate on + * @argp: command payload + * +- * Return: Newly created bus on success, ERR_PTR on failure. ++ * Return: NULL or newly created bus on success, ERR_PTR on failure. + */ + struct kdbus_bus *kdbus_cmd_bus_make(struct kdbus_domain *domain, + void __user *argp) +@@ -459,7 +459,7 @@ exit: + * @conn: connection to operate on + * @argp: command payload + * +- * Return: 0 on success, negative error code on failure. ++ * Return: >=0 on success, negative error code on failure. + */ + int kdbus_cmd_bus_creator_info(struct kdbus_conn *conn, void __user *argp) + { +diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c +index cbfbf3847c24..8ee62fc0bd46 100644 +--- a/ipc/kdbus/connection.c ++++ b/ipc/kdbus/connection.c +@@ -1589,7 +1589,7 @@ bool kdbus_conn_policy_see_notification(struct kdbus_conn *conn, + * @privileged: Whether the caller is privileged + * @argp: Command payload + * +- * Return: Newly created connection on success, ERR_PTR on failure. ++ * Return: NULL or newly created connection on success, ERR_PTR on failure. + */ + struct kdbus_conn *kdbus_cmd_hello(struct kdbus_ep *ep, bool privileged, + void __user *argp) +@@ -1676,7 +1676,7 @@ exit: + * + * The caller must not hold any active reference to @conn or this will deadlock. + * +- * Return: 0 on success, negative error code on failure. ++ * Return: >=0 on success, negative error code on failure. + */ + int kdbus_cmd_byebye_unlocked(struct kdbus_conn *conn, void __user *argp) + { +@@ -1708,7 +1708,7 @@ int kdbus_cmd_byebye_unlocked(struct kdbus_conn *conn, void __user *argp) + * @conn: connection to operate on + * @argp: command payload + * +- * Return: 0 on success, negative error code on failure. ++ * Return: >=0 on success, negative error code on failure. + */ + int kdbus_cmd_conn_info(struct kdbus_conn *conn, void __user *argp) + { +@@ -1838,7 +1838,7 @@ exit: + * @conn: connection to operate on + * @argp: command payload + * +- * Return: 0 on success, negative error code on failure. ++ * Return: >=0 on success, negative error code on failure. + */ + int kdbus_cmd_update(struct kdbus_conn *conn, void __user *argp) + { +@@ -1935,7 +1935,7 @@ exit: + * @f: file this command was called on + * @argp: command payload + * +- * Return: 0 on success, negative error code on failure. ++ * Return: >=0 on success, negative error code on failure. + */ + int kdbus_cmd_send(struct kdbus_conn *conn, struct file *f, void __user *argp) + { +@@ -2031,7 +2031,7 @@ exit: + * @conn: connection to operate on + * @argp: command payload + * +- * Return: 0 on success, negative error code on failure. ++ * Return: >=0 on success, negative error code on failure. + */ + int kdbus_cmd_recv(struct kdbus_conn *conn, void __user *argp) + { +@@ -2154,7 +2154,7 @@ exit: + * @conn: connection to operate on + * @argp: command payload + * +- * Return: 0 on success, negative error code on failure. ++ * Return: >=0 on success, negative error code on failure. + */ + int kdbus_cmd_free(struct kdbus_conn *conn, void __user *argp) + { +diff --git a/ipc/kdbus/endpoint.c b/ipc/kdbus/endpoint.c +index 174d274b113e..9a95a5ea84d7 100644 +--- a/ipc/kdbus/endpoint.c ++++ b/ipc/kdbus/endpoint.c +@@ -188,7 +188,7 @@ struct kdbus_ep *kdbus_ep_unref(struct kdbus_ep *ep) + * @bus: bus to operate on + * @argp: command payload + * +- * Return: Newly created endpoint on success, ERR_PTR on failure. ++ * Return: NULL or newly created endpoint on success, ERR_PTR on failure. + */ + struct kdbus_ep *kdbus_cmd_ep_make(struct kdbus_bus *bus, void __user *argp) + { +@@ -247,7 +247,7 @@ exit: + * @ep: endpoint to operate on + * @argp: command payload + * +- * Return: Newly created endpoint on success, ERR_PTR on failure. ++ * Return: >=0 on success, negative error code on failure. + */ + int kdbus_cmd_ep_update(struct kdbus_ep *ep, void __user *argp) + { +diff --git a/ipc/kdbus/match.c b/ipc/kdbus/match.c +index 30cec1ca819f..cc083b4211de 100644 +--- a/ipc/kdbus/match.c ++++ b/ipc/kdbus/match.c +@@ -368,7 +368,7 @@ static int kdbus_match_db_remove_unlocked(struct kdbus_match_db *mdb, + * are used to match messages from userspace, while the others apply to + * kernel-generated notifications. + * +- * Return: 0 on success, negative error code on failure. ++ * Return: >=0 on success, negative error code on failure. + */ + int kdbus_cmd_match_add(struct kdbus_conn *conn, void __user *argp) + { +@@ -528,7 +528,7 @@ exit: + * @conn: connection to operate on + * @argp: command payload + * +- * Return: 0 on success, negative error code on failure. ++ * Return: >=0 on success, negative error code on failure. + */ + int kdbus_cmd_match_remove(struct kdbus_conn *conn, void __user *argp) + { +diff --git a/ipc/kdbus/names.c b/ipc/kdbus/names.c +index 657008e1bb37..5f5d84ea0e8e 100644 +--- a/ipc/kdbus/names.c ++++ b/ipc/kdbus/names.c +@@ -469,7 +469,7 @@ void kdbus_name_release_all(struct kdbus_name_registry *reg, + * @conn: connection to operate on + * @argp: command payload + * +- * Return: 0 on success, negative error code on failure. ++ * Return: >=0 on success, negative error code on failure. + */ + int kdbus_cmd_name_acquire(struct kdbus_conn *conn, void __user *argp) + { +@@ -528,7 +528,7 @@ exit: + * @conn: connection to operate on + * @argp: command payload + * +- * Return: 0 on success, negative error code on failure. ++ * Return: >=0 on success, negative error code on failure. + */ + int kdbus_cmd_name_release(struct kdbus_conn *conn, void __user *argp) + { +@@ -699,7 +699,7 @@ static int kdbus_list_all(struct kdbus_conn *conn, u64 flags, + * @conn: connection to operate on + * @argp: command payload + * +- * Return: 0 on success, negative error code on failure. ++ * Return: >=0 on success, negative error code on failure. + */ + int kdbus_cmd_list(struct kdbus_conn *conn, void __user *argp) + { diff --git a/kdbus-forward-ID-notifications-to-everyone.patch b/kdbus-forward-ID-notifications-to-everyone.patch new file mode 100644 index 000000000..b9cd4daa1 --- /dev/null +++ b/kdbus-forward-ID-notifications-to-everyone.patch @@ -0,0 +1,88 @@ +From: David Herrmann <dh.herrmann@gmail.com> +Date: Tue, 26 May 2015 09:30:14 +0200 +Subject: [PATCH] kdbus: forward ID notifications to everyone + +Even if you cannot SEE another peer (eg., if you're behind a private +endpoint), the other peer might be able to TALK to you. Therefore, you +might get messages from them. This works mostly fine, with one major +exception, that you cannot track the remote peer. You will not receive ID +notifications for it, thus, you don't get notified when they disconnect. +This is unforunate and breaks sandboxes kdbus peers. + +Fix this by forwarding ID notifications to everyone. Note that those +notifications don't carry _any_ useful information, besides the peer ID. +Therefore, even if you should not able to SEE a peer, you will now still +get ID notifications. This does not reveal any additional information on +the remote peer, besides its lifetime. Hence, it should be fine. + +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Acked-by: Daniel Mack <daniel@zonque.org> +--- + ipc/kdbus/connection.c | 8 +++----- + tools/testing/selftests/kdbus/test-endpoint.c | 13 ++++++++++++- + 2 files changed, 15 insertions(+), 6 deletions(-) + +diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c +index fb2c6c67c4c1..272b991f36f4 100644 +--- a/ipc/kdbus/connection.c ++++ b/ipc/kdbus/connection.c +@@ -1588,10 +1588,8 @@ bool kdbus_conn_policy_see_notification(struct kdbus_conn *conn, + * to a peer if, and only if, that peer can see the name this + * notification is for. + * +- * KDBUS_ITEM_ID_{ADD,REMOVE}: As new peers cannot have names, and all +- * names are dropped before a peer is removed, those notifications +- * cannot be seen on custom endpoints. Thus, we only pass them +- * through on default endpoints. ++ * KDBUS_ITEM_ID_{ADD,REMOVE}: Notifications for ID changes are ++ * broadcast to everyone, to allow tracking peers. + */ + + switch (kmsg->notify_type) { +@@ -1603,7 +1601,7 @@ bool kdbus_conn_policy_see_notification(struct kdbus_conn *conn, + + case KDBUS_ITEM_ID_ADD: + case KDBUS_ITEM_ID_REMOVE: +- return !conn->ep->user; ++ return true; + + default: + WARN(1, "Invalid type for notification broadcast: %llu\n", +diff --git a/tools/testing/selftests/kdbus/test-endpoint.c b/tools/testing/selftests/kdbus/test-endpoint.c +index dcc6ab91c4e6..34a7be49c482 100644 +--- a/tools/testing/selftests/kdbus/test-endpoint.c ++++ b/tools/testing/selftests/kdbus/test-endpoint.c +@@ -255,6 +255,13 @@ int kdbus_test_custom_endpoint(struct kdbus_test_env *env) + ep_conn = kdbus_hello(ep, 0, NULL, 0); + ASSERT_RETURN(ep_conn); + ++ /* Check that the reader got the IdAdd notification */ ++ ret = kdbus_msg_recv(reader, &msg, NULL); ++ ASSERT_RETURN(ret == 0); ++ ASSERT_RETURN(msg->items[0].type == KDBUS_ITEM_ID_ADD); ++ ASSERT_RETURN(msg->items[0].id_change.id == ep_conn->id); ++ kdbus_msg_free(msg); ++ + /* + * Add a name add match on the endpoint connection, acquire name from + * the unfiltered connection, and make sure the filtered connection +@@ -283,7 +290,7 @@ int kdbus_test_custom_endpoint(struct kdbus_test_env *env) + ret = kdbus_conn_info(ep_conn, 0x0fffffffffffffffULL, NULL, 0, NULL); + ASSERT_RETURN(ret == -ENXIO); + +- /* Check that the reader did not receive anything */ ++ /* Check that the reader did not receive the name notification */ + ret = kdbus_msg_recv(reader, NULL, NULL); + ASSERT_RETURN(ret == -EAGAIN); + +@@ -295,6 +302,10 @@ int kdbus_test_custom_endpoint(struct kdbus_test_env *env) + ret = kdbus_name_release(env->conn, name); + ASSERT_RETURN(ret == 0); + ++ /* Check that the reader did not receive the name notification */ ++ ret = kdbus_msg_recv(reader, NULL, NULL); ++ ASSERT_RETURN(ret == -EAGAIN); ++ + ret = update_endpoint(ep_fd, name); + ASSERT_RETURN(ret == 0); + diff --git a/kdbus-kdbus_conn_connect-use-bus-instead-of-conn-ep-.patch b/kdbus-kdbus_conn_connect-use-bus-instead-of-conn-ep-.patch new file mode 100644 index 000000000..d009e924f --- /dev/null +++ b/kdbus-kdbus_conn_connect-use-bus-instead-of-conn-ep-.patch @@ -0,0 +1,27 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Tue, 2 Jun 2015 18:48:50 +0300 +Subject: [PATCH] kdbus: kdbus_conn_connect(): use `bus' instead of + `conn->ep->bus' + +Local `bus' is already set to `conn->ep->bus'. Use it. + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + ipc/kdbus/connection.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c +index 1bd7bb968f9f..707be050b408 100644 +--- a/ipc/kdbus/connection.c ++++ b/ipc/kdbus/connection.c +@@ -432,7 +432,7 @@ static int kdbus_conn_connect(struct kdbus_conn *conn, const char *name) + * directly, and won't cause any notifications. + */ + if (!kdbus_conn_is_monitor(conn)) { +- ret = kdbus_notify_id_change(conn->ep->bus, KDBUS_ITEM_ID_ADD, ++ ret = kdbus_notify_id_change(bus, KDBUS_ITEM_ID_ADD, + conn->id, conn->flags); + if (ret < 0) + goto exit_disconnect; diff --git a/kdbus-kdbus_item_validate-remove-duplicated-code.patch b/kdbus-kdbus_item_validate-remove-duplicated-code.patch new file mode 100644 index 000000000..f00aa6eca --- /dev/null +++ b/kdbus-kdbus_item_validate-remove-duplicated-code.patch @@ -0,0 +1,31 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Tue, 2 Jun 2015 18:48:49 +0300 +Subject: [PATCH] kdbus: kdbus_item_validate(): remove duplicated code + +KDBUS_ITEM_PAYLOAD_VEC and KDBUS_ITEM_PAYLOAD_OFF cases use literally +the same code, so merge them. + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + ipc/kdbus/item.c | 6 ------ + 1 file changed, 6 deletions(-) + +diff --git a/ipc/kdbus/item.c b/ipc/kdbus/item.c +index 745ad5495096..1ee72c2ad7c3 100644 +--- a/ipc/kdbus/item.c ++++ b/ipc/kdbus/item.c +@@ -96,12 +96,6 @@ int kdbus_item_validate(const struct kdbus_item *item) + break; + + 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; diff --git a/kdbus-kdbus_reply_find-return-on-found-entry.patch b/kdbus-kdbus_reply_find-return-on-found-entry.patch new file mode 100644 index 000000000..874ef52ed --- /dev/null +++ b/kdbus-kdbus_reply_find-return-on-found-entry.patch @@ -0,0 +1,42 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Wed, 17 Jun 2015 20:14:56 +0300 +Subject: [PATCH] kdbus: kdbus_reply_find(): return on found entry + +Return found entry immediately instead of assigning it to additional +variable and breaking the loop. It's simpler to read, the same way is +used in kdbus_conn_has_name(), for example. + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Reviewed-by: Djalal Harouni <tixxdz@opendz.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + ipc/kdbus/reply.c | 10 ++++------ + 1 file changed, 4 insertions(+), 6 deletions(-) + +diff --git a/ipc/kdbus/reply.c b/ipc/kdbus/reply.c +index 89d355b44f63..9d823ebee71f 100644 +--- a/ipc/kdbus/reply.c ++++ b/ipc/kdbus/reply.c +@@ -171,17 +171,15 @@ struct kdbus_reply *kdbus_reply_find(struct kdbus_conn *replying, + struct kdbus_conn *reply_dst, + u64 cookie) + { +- struct kdbus_reply *r, *reply = NULL; ++ struct kdbus_reply *r; + + list_for_each_entry(r, &reply_dst->reply_list, entry) { + if (r->cookie == cookie && +- (!replying || r->reply_src == replying)) { +- reply = r; +- break; +- } ++ (!replying || r->reply_src == replying)) ++ return r; + } + +- return reply; ++ return NULL; + } + + /** diff --git a/kdbus-make-metadata-on-broadcasts-reliable.patch b/kdbus-make-metadata-on-broadcasts-reliable.patch new file mode 100644 index 000000000..e76f429c5 --- /dev/null +++ b/kdbus-make-metadata-on-broadcasts-reliable.patch @@ -0,0 +1,61 @@ +From: David Herrmann <dh.herrmann@gmail.com> +Date: Tue, 26 May 2015 10:01:37 +0200 +Subject: [PATCH] kdbus: make metadata on broadcasts reliable + +If we cannot collect metadata, this is a serious error. Don't try to +continue sending a message, but immediately bail out and tell the receiver +that we dropped it. Otherwise, the receiver cannot rely on metadata to be +present and might assume it's a faked connection. + +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Acked-by: Daniel Mack <daniel@zonque.org> +--- + ipc/kdbus/bus.c | 28 ++++++++++++++-------------- + 1 file changed, 14 insertions(+), 14 deletions(-) + +diff --git a/ipc/kdbus/bus.c b/ipc/kdbus/bus.c +index 9a0ecbc9df2f..d5475961b896 100644 +--- a/ipc/kdbus/bus.c ++++ b/ipc/kdbus/bus.c +@@ -293,12 +293,12 @@ void kdbus_bus_broadcast(struct kdbus_bus *bus, + if (!kdbus_conn_policy_talk(conn_dst, NULL, conn_src)) + continue; + +- /* +- * Keep sending messages even if we cannot acquire the +- * requested metadata. It's up to the receiver to drop +- * messages that lack expected metadata. +- */ +- kdbus_kmsg_collect_metadata(kmsg, conn_src, conn_dst); ++ ret = kdbus_kmsg_collect_metadata(kmsg, conn_src, ++ conn_dst); ++ if (ret < 0) { ++ kdbus_conn_lost_message(conn_dst); ++ continue; ++ } + } else { + /* + * Check if there is a policy db that prevents the +@@ -344,14 +344,14 @@ void kdbus_bus_eavesdrop(struct kdbus_bus *bus, + + down_read(&bus->conn_rwlock); + list_for_each_entry(conn_dst, &bus->monitors_list, monitor_entry) { +- /* +- * Collect metadata requested by the destination connection. +- * Ignore errors, as receivers need to check metadata +- * availability, anyway. So it's still better to send messages +- * that lack data, than to skip it entirely. +- */ +- if (conn_src) +- kdbus_kmsg_collect_metadata(kmsg, conn_src, conn_dst); ++ if (conn_src) { ++ ret = kdbus_kmsg_collect_metadata(kmsg, conn_src, ++ conn_dst); ++ if (ret < 0) { ++ kdbus_conn_lost_message(conn_dst); ++ continue; ++ } ++ } + + ret = kdbus_conn_entry_insert(conn_src, conn_dst, kmsg, NULL); + if (ret < 0) diff --git a/kdbus-no-need-to-ref-current-mm.patch b/kdbus-no-need-to-ref-current-mm.patch new file mode 100644 index 000000000..0f5cbed40 --- /dev/null +++ b/kdbus-no-need-to-ref-current-mm.patch @@ -0,0 +1,67 @@ +From: David Herrmann <dh.herrmann@gmail.com> +Date: Mon, 20 Apr 2015 11:13:54 +0200 +Subject: [PATCH] kdbus: no need to ref current->mm + +If we access current->mm temporarily, there is no need to ref it. It can +only be changed by us, so no-one can race with us. + +Avoid ref'ing and unref'ing it just to access some of its fields, similar +to what syscalls in mm/ do. + +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +--- + ipc/kdbus/metadata.c | 21 +++------------------ + 1 file changed, 3 insertions(+), 18 deletions(-) + +diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c +index a85eac34a5c4..c36b9cc67637 100644 +--- a/ipc/kdbus/metadata.c ++++ b/ipc/kdbus/metadata.c +@@ -282,15 +282,10 @@ static void kdbus_meta_proc_collect_pid_comm(struct kdbus_meta_proc *mp) + + static void kdbus_meta_proc_collect_exe(struct kdbus_meta_proc *mp) + { +- struct mm_struct *mm; + struct file *exe_file; + +- mm = get_task_mm(current); +- if (!mm) +- return; +- + rcu_read_lock(); +- exe_file = rcu_dereference(mm->exe_file); ++ exe_file = rcu_dereference(current->mm->exe_file); + if (exe_file) { + mp->exe_path = exe_file->f_path; + path_get(&mp->exe_path); +@@ -298,28 +293,18 @@ static void kdbus_meta_proc_collect_exe(struct kdbus_meta_proc *mp) + mp->valid |= KDBUS_ATTACH_EXE; + } + rcu_read_unlock(); +- +- mmput(mm); + } + + static int kdbus_meta_proc_collect_cmdline(struct kdbus_meta_proc *mp) + { +- struct mm_struct *mm; ++ struct mm_struct *mm = current->mm; + char *cmdline; + +- mm = get_task_mm(current); +- if (!mm) +- return 0; +- +- if (!mm->arg_end) { +- mmput(mm); ++ if (!mm->arg_end) + return 0; +- } + + cmdline = strndup_user((const char __user *)mm->arg_start, + mm->arg_end - mm->arg_start); +- mmput(mm); +- + if (IS_ERR(cmdline)) + return PTR_ERR(cmdline); + diff --git a/kdbus-optimize-auxgroup-collector.patch b/kdbus-optimize-auxgroup-collector.patch new file mode 100644 index 000000000..ea6505525 --- /dev/null +++ b/kdbus-optimize-auxgroup-collector.patch @@ -0,0 +1,50 @@ +From: David Herrmann <dh.herrmann@gmail.com> +Date: Mon, 20 Apr 2015 10:53:35 +0200 +Subject: [PATCH] kdbus: optimize auxgroup collector + +current->creds can only be changed by 'current'. That is, as long as we +only access our own credentials, we can be sure it does not change. Hence, +there is no need to ref cred->group_info if all we do is copy its content. + +This avoids touching shared cachelines when collecting auxgroups. + +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Acked-by: Daniel Mack <daniel@zonque.org> +--- + ipc/kdbus/metadata.c | 10 ++++------ + 1 file changed, 4 insertions(+), 6 deletions(-) + +diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c +index eeebfef11552..174436f0aa01 100644 +--- a/ipc/kdbus/metadata.c ++++ b/ipc/kdbus/metadata.c +@@ -245,25 +245,23 @@ static void kdbus_meta_proc_collect_pids(struct kdbus_meta_proc *mp) + + static int kdbus_meta_proc_collect_auxgroups(struct kdbus_meta_proc *mp) + { +- struct group_info *info; ++ const struct group_info *info; + size_t i; + +- info = get_current_groups(); ++ /* no need to lock/ref, current creds cannot change */ ++ info = current_cred()->group_info; + + if (info->ngroups > 0) { + mp->auxgrps = kmalloc_array(info->ngroups, sizeof(kgid_t), + GFP_KERNEL); +- if (!mp->auxgrps) { +- put_group_info(info); ++ if (!mp->auxgrps) + return -ENOMEM; +- } + + for (i = 0; i < info->ngroups; i++) + mp->auxgrps[i] = GROUP_AT(info, i); + } + + mp->n_auxgrps = info->ngroups; +- put_group_info(info); + mp->valid |= KDBUS_ATTACH_AUXGROUPS; + + return 0; diff --git a/kdbus-optimize-error-path-in-kdbus_reply_new.patch b/kdbus-optimize-error-path-in-kdbus_reply_new.patch new file mode 100644 index 000000000..25c416208 --- /dev/null +++ b/kdbus-optimize-error-path-in-kdbus_reply_new.patch @@ -0,0 +1,46 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Wed, 17 Jun 2015 20:14:57 +0300 +Subject: [PATCH] kdbus: optimize error path in kdbus_reply_new() + +Move cleanup code to separate location as it never executes on normal +flow. This removes extra if-block and the need to initialize `ret'. + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Reviewed-by: Djalal Harouni <tixxdz@opendz.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + ipc/kdbus/reply.c | 12 +++++------- + 1 file changed, 5 insertions(+), 7 deletions(-) + +diff --git a/ipc/kdbus/reply.c b/ipc/kdbus/reply.c +index 9d823ebee71f..e6791d86ec92 100644 +--- a/ipc/kdbus/reply.c ++++ b/ipc/kdbus/reply.c +@@ -37,7 +37,7 @@ struct kdbus_reply *kdbus_reply_new(struct kdbus_conn *reply_src, + bool sync) + { + struct kdbus_reply *r; +- int ret = 0; ++ int ret; + + if (atomic_inc_return(&reply_dst->request_count) > + KDBUS_CONN_MAX_REQUESTS_PENDING) { +@@ -64,13 +64,11 @@ struct kdbus_reply *kdbus_reply_new(struct kdbus_conn *reply_src, + r->waiting = true; + } + +-exit_dec_request_count: +- if (ret < 0) { +- atomic_dec(&reply_dst->request_count); +- return ERR_PTR(ret); +- } +- + return r; ++ ++exit_dec_request_count: ++ atomic_dec(&reply_dst->request_count); ++ return ERR_PTR(ret); + } + + static void __kdbus_reply_free(struct kref *kref) diff --git a/kdbus-optimize-if-statements-in-kdbus_conn_disconnec.patch b/kdbus-optimize-if-statements-in-kdbus_conn_disconnec.patch new file mode 100644 index 000000000..ed414805b --- /dev/null +++ b/kdbus-optimize-if-statements-in-kdbus_conn_disconnec.patch @@ -0,0 +1,48 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Wed, 17 Jun 2015 20:14:58 +0300 +Subject: [PATCH] kdbus: optimize if statements in kdbus_conn_disconnect() + +if (r->sync) branch and code after it both call kdbus_reply_unlink(). +Rewrite them as if-else to eliminate code duplication and make algorithm +more obvious. + +Convert outer if statement to use continue in order to reduce +indentation and make things easier to read. + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Reviewed-by: Djalal Harouni <tixxdz@opendz.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + ipc/kdbus/connection.c | 15 +++++++-------- + 1 file changed, 7 insertions(+), 8 deletions(-) + +diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c +index 707be050b408..9993753d11de 100644 +--- a/ipc/kdbus/connection.c ++++ b/ipc/kdbus/connection.c +@@ -559,17 +559,16 @@ int kdbus_conn_disconnect(struct kdbus_conn *conn, bool ensure_queue_empty) + hash_for_each(bus->conn_hash, i, c, hentry) { + mutex_lock(&c->lock); + list_for_each_entry_safe(r, r_tmp, &c->reply_list, entry) { +- if (r->reply_src == conn) { +- if (r->sync) { +- kdbus_sync_reply_wakeup(r, -EPIPE); +- kdbus_reply_unlink(r); +- continue; +- } ++ if (r->reply_src != conn) ++ continue; + ++ if (r->sync) ++ kdbus_sync_reply_wakeup(r, -EPIPE); ++ else + /* send a 'connection dead' notification */ + kdbus_notify_reply_dead(bus, c->id, r->cookie); +- kdbus_reply_unlink(r); +- } ++ ++ kdbus_reply_unlink(r); + } + mutex_unlock(&c->lock); + } diff --git a/kdbus-pool-use-__vfs_read.patch b/kdbus-pool-use-__vfs_read.patch new file mode 100644 index 000000000..12813f4df --- /dev/null +++ b/kdbus-pool-use-__vfs_read.patch @@ -0,0 +1,72 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Tue, 21 Apr 2015 02:12:18 +0300 +Subject: [PATCH] kdbus: pool: use __vfs_read() + +After commit 5d5d56897530 ("make new_sync_{read,write}() static") +->read() cannot be called directly. + +kdbus_pool_slice_copy() leads to oops, which can be reproduced by +launching tools/testing/selftests/kdbus/kdbus-test -t message-quota: + +[ 1167.146793] BUG: unable to handle kernel NULL pointer dereference at (null) +[ 1167.147554] IP: [< (null)>] (null) +[ 1167.148670] PGD 3a9dd067 PUD 3a841067 PMD 0 +[ 1167.149611] Oops: 0010 [#1] SMP +[ 1167.150088] Modules linked in: nfsv3 nfs kdbus lockd grace sunrpc +[ 1167.150771] CPU: 0 PID: 518 Comm: kdbus-test Not tainted 4.0.0-next-20150420-kdbus #62 +[ 1167.150771] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 +[ 1167.150771] task: ffff88003daed120 ti: ffff88003a800000 task.ti: ffff88003a800000 +[ 1167.150771] RIP: 0010:[<0000000000000000>] [< (null)>] (null) +[ 1167.150771] RSP: 0018:ffff88003a803bc0 EFLAGS: 00010286 +[ 1167.150771] RAX: ffff8800377fb000 RBX: 00000000000201e8 RCX: ffff88003a803c00 +[ 1167.150771] RDX: 0000000000000b40 RSI: ffff8800377fb4c0 RDI: ffff88003d815700 +[ 1167.150771] RBP: ffff88003a803c48 R08: ffffffff8139e380 R09: ffff880039d80490 +[ 1167.150771] R10: ffff88003a803a90 R11: 00000000000004c0 R12: 00000000002a24c0 +[ 1167.150771] R13: 0000000000000b40 R14: ffff88003d815700 R15: ffffffff8139e460 +[ 1167.150771] FS: 00007f41dccd4740(0000) GS:ffff88003fc00000(0000) knlGS:0000000000000000 +[ 1167.150771] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +[ 1167.150771] CR2: 0000000000000000 CR3: 000000003ccdf000 CR4: 00000000000007b0 +[ 1167.150771] Stack: +[ 1167.150771] ffffffffa0065497 ffff88003a803c10 00007ffffffff000 ffff88003aaa67c0 +[ 1167.150771] 00000000000004c0 ffff88003aaa6870 ffff88003ca83300 ffffffffa006537d +[ 1167.150771] 00000000000201e8 ffffea0000ddfec0 ffff88003a803c20 0000000000000018 +[ 1167.150771] Call Trace: +[ 1167.150771] [<ffffffffa0065497>] ? kdbus_pool_slice_copy+0x127/0x200 [kdbus] +[ 1167.150771] [<ffffffffa006537d>] ? kdbus_pool_slice_copy+0xd/0x200 [kdbus] +[ 1167.150771] [<ffffffffa006670a>] kdbus_queue_entry_move+0xaa/0x180 [kdbus] +[ 1167.150771] [<ffffffffa0059e64>] kdbus_conn_move_messages+0x1e4/0x2c0 [kdbus] +[ 1167.150771] [<ffffffffa006234e>] kdbus_name_acquire+0x31e/0x390 [kdbus] +[ 1167.150771] [<ffffffffa00625c5>] kdbus_cmd_name_acquire+0x125/0x130 [kdbus] +[ 1167.150771] [<ffffffffa005db5d>] kdbus_handle_ioctl+0x4ed/0x610 [kdbus] +[ 1167.150771] [<ffffffff811040e0>] do_vfs_ioctl+0x2e0/0x4e0 +[ 1167.150771] [<ffffffff81389750>] ? preempt_schedule_common+0x1f/0x3f +[ 1167.150771] [<ffffffff8110431c>] SyS_ioctl+0x3c/0x80 +[ 1167.150771] [<ffffffff8138c36e>] system_call_fastpath+0x12/0x71 +[ 1167.150771] Code: Bad RIP value. +[ 1167.150771] RIP [< (null)>] (null) +[ 1167.150771] RSP <ffff88003a803bc0> +[ 1167.150771] CR2: 0000000000000000 +[ 1167.168756] ---[ end trace a676bcfa75db5a96 ]--- + +Use __vfs_read() instead. + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + ipc/kdbus/pool.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipc/kdbus/pool.c b/ipc/kdbus/pool.c +index 139bb77056b3..45dcdea505f4 100644 +--- a/ipc/kdbus/pool.c ++++ b/ipc/kdbus/pool.c +@@ -675,7 +675,7 @@ int kdbus_pool_slice_copy(const struct kdbus_pool_slice *slice_dst, + } + + kaddr = (char __force __user *)kmap(page) + page_off; +- n_read = f_src->f_op->read(f_src, kaddr, copy_len, &off_src); ++ n_read = __vfs_read(f_src, kaddr, copy_len, &off_src); + kunmap(page); + mark_page_accessed(page); + flush_dcache_page(page); diff --git a/kdbus-provide-helper-to-collect-metadata.patch b/kdbus-provide-helper-to-collect-metadata.patch new file mode 100644 index 000000000..0e02b3011 --- /dev/null +++ b/kdbus-provide-helper-to-collect-metadata.patch @@ -0,0 +1,195 @@ +From: David Herrmann <dh.herrmann@gmail.com> +Date: Tue, 26 May 2015 09:59:02 +0200 +Subject: [PATCH] kdbus: provide helper to collect metadata + +Provide a new helper kdbus_kmsg_collect_metadata() which implements the +common task of collecting proc- and conn-metadata on a kmsg. + +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Acked-by: Daniel Mack <daniel@zonque.org> +--- + ipc/kdbus/bus.c | 24 +++--------------------- + ipc/kdbus/connection.c | 35 ++++------------------------------- + ipc/kdbus/message.c | 24 ++++++++++++++++++++++++ + ipc/kdbus/message.h | 2 ++ + 4 files changed, 33 insertions(+), 52 deletions(-) + +diff --git a/ipc/kdbus/bus.c b/ipc/kdbus/bus.c +index 9d0679eb59f6..9a0ecbc9df2f 100644 +--- a/ipc/kdbus/bus.c ++++ b/ipc/kdbus/bus.c +@@ -285,8 +285,6 @@ void kdbus_bus_broadcast(struct kdbus_bus *bus, + continue; + + if (conn_src) { +- u64 attach_flags; +- + /* + * Anyone can send broadcasts, as they have no + * destination. But a receiver needs TALK access to +@@ -295,19 +293,12 @@ void kdbus_bus_broadcast(struct kdbus_bus *bus, + if (!kdbus_conn_policy_talk(conn_dst, NULL, conn_src)) + continue; + +- attach_flags = kdbus_meta_calc_attach_flags(conn_src, +- conn_dst); +- + /* + * Keep sending messages even if we cannot acquire the + * requested metadata. It's up to the receiver to drop + * messages that lack expected metadata. + */ +- if (!conn_src->faked_meta) +- kdbus_meta_proc_collect(kmsg->proc_meta, +- attach_flags); +- kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, conn_src, +- attach_flags); ++ kdbus_kmsg_collect_metadata(kmsg, conn_src, conn_dst); + } else { + /* + * Check if there is a policy db that prevents the +@@ -359,17 +350,8 @@ void kdbus_bus_eavesdrop(struct kdbus_bus *bus, + * availability, anyway. So it's still better to send messages + * that lack data, than to skip it entirely. + */ +- if (conn_src) { +- u64 attach_flags; +- +- attach_flags = kdbus_meta_calc_attach_flags(conn_src, +- conn_dst); +- if (!conn_src->faked_meta) +- kdbus_meta_proc_collect(kmsg->proc_meta, +- attach_flags); +- kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, conn_src, +- attach_flags); +- } ++ if (conn_src) ++ kdbus_kmsg_collect_metadata(kmsg, conn_src, conn_dst); + + ret = kdbus_conn_entry_insert(conn_src, conn_dst, kmsg, NULL); + if (ret < 0) +diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c +index 272b991f36f4..cbfbf3847c24 100644 +--- a/ipc/kdbus/connection.c ++++ b/ipc/kdbus/connection.c +@@ -1098,7 +1098,6 @@ static int kdbus_conn_reply(struct kdbus_conn *src, struct kdbus_kmsg *kmsg) + struct kdbus_reply *reply, *wake = NULL; + struct kdbus_conn *dst = NULL; + struct kdbus_bus *bus = src->ep->bus; +- u64 attach; + int ret; + + if (WARN_ON(kmsg->msg.dst_id == KDBUS_DST_ID_BROADCAST) || +@@ -1131,15 +1130,7 @@ static int kdbus_conn_reply(struct kdbus_conn *src, struct kdbus_kmsg *kmsg) + + /* attach metadata */ + +- attach = kdbus_meta_calc_attach_flags(src, dst); +- +- if (!src->faked_meta) { +- ret = kdbus_meta_proc_collect(kmsg->proc_meta, attach); +- if (ret < 0) +- goto exit; +- } +- +- ret = kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, src, attach); ++ ret = kdbus_kmsg_collect_metadata(kmsg, src, dst); + if (ret < 0) + goto exit; + +@@ -1167,7 +1158,6 @@ static struct kdbus_reply *kdbus_conn_call(struct kdbus_conn *src, + struct kdbus_reply *wait = NULL; + struct kdbus_conn *dst = NULL; + struct kdbus_bus *bus = src->ep->bus; +- u64 attach; + int ret; + + if (WARN_ON(kmsg->msg.dst_id == KDBUS_DST_ID_BROADCAST) || +@@ -1218,15 +1208,7 @@ static struct kdbus_reply *kdbus_conn_call(struct kdbus_conn *src, + + /* attach metadata */ + +- attach = kdbus_meta_calc_attach_flags(src, dst); +- +- if (!src->faked_meta) { +- ret = kdbus_meta_proc_collect(kmsg->proc_meta, attach); +- if (ret < 0) +- goto exit; +- } +- +- ret = kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, src, attach); ++ ret = kdbus_kmsg_collect_metadata(kmsg, src, dst); + if (ret < 0) + goto exit; + +@@ -1257,7 +1239,6 @@ static int kdbus_conn_unicast(struct kdbus_conn *src, struct kdbus_kmsg *kmsg) + struct kdbus_conn *dst = NULL; + struct kdbus_bus *bus = src->ep->bus; + bool is_signal = (kmsg->msg.flags & KDBUS_MSG_SIGNAL); +- u64 attach; + int ret = 0; + + if (WARN_ON(kmsg->msg.dst_id == KDBUS_DST_ID_BROADCAST) || +@@ -1296,16 +1277,8 @@ static int kdbus_conn_unicast(struct kdbus_conn *src, struct kdbus_kmsg *kmsg) + + /* attach metadata */ + +- attach = kdbus_meta_calc_attach_flags(src, dst); +- +- if (!src->faked_meta) { +- ret = kdbus_meta_proc_collect(kmsg->proc_meta, attach); +- if (ret < 0 && !is_signal) +- goto exit; +- } +- +- ret = kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, src, attach); +- if (ret < 0 && !is_signal) ++ ret = kdbus_kmsg_collect_metadata(kmsg, src, dst); ++ if (ret < 0) + goto exit; + + /* send message */ +diff --git a/ipc/kdbus/message.c b/ipc/kdbus/message.c +index 80960756a329..066e816dfdea 100644 +--- a/ipc/kdbus/message.c ++++ b/ipc/kdbus/message.c +@@ -614,3 +614,27 @@ exit_free: + kdbus_kmsg_free(m); + return ERR_PTR(ret); + } ++ ++/** ++ * kdbus_kmsg_collect_metadata() - collect metadata ++ * @kmsg: message to collect metadata on ++ * @src: source connection of message ++ * @dst: destination connection of message ++ * ++ * Return: 0 on success, negative error code on failure. ++ */ ++int kdbus_kmsg_collect_metadata(struct kdbus_kmsg *kmsg, struct kdbus_conn *src, ++ struct kdbus_conn *dst) ++{ ++ u64 attach; ++ int ret; ++ ++ attach = kdbus_meta_calc_attach_flags(src, dst); ++ if (!src->faked_meta) { ++ ret = kdbus_meta_proc_collect(kmsg->proc_meta, attach); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, src, attach); ++} +diff --git a/ipc/kdbus/message.h b/ipc/kdbus/message.h +index af4775850235..cdaa65c4e6ae 100644 +--- a/ipc/kdbus/message.h ++++ b/ipc/kdbus/message.h +@@ -129,5 +129,7 @@ struct kdbus_kmsg *kdbus_kmsg_new(struct kdbus_bus *bus, size_t extra_size); + struct kdbus_kmsg *kdbus_kmsg_new_from_cmd(struct kdbus_conn *conn, + struct kdbus_cmd_send *cmd_send); + void kdbus_kmsg_free(struct kdbus_kmsg *kmsg); ++int kdbus_kmsg_collect_metadata(struct kdbus_kmsg *kmsg, struct kdbus_conn *src, ++ struct kdbus_conn *dst); + + #endif diff --git a/kdbus-reduce-scope-of-handle-locking.patch b/kdbus-reduce-scope-of-handle-locking.patch new file mode 100644 index 000000000..7cca4f787 --- /dev/null +++ b/kdbus-reduce-scope-of-handle-locking.patch @@ -0,0 +1,276 @@ +From: David Herrmann <dh.herrmann@gmail.com> +Date: Sat, 18 Apr 2015 12:39:51 +0200 +Subject: [PATCH] kdbus: reduce scope of handle locking + +A kdbus handle is used to create objects in the kdbus hierarchy. During +open(), we do not have enough information to know how to setup the object. +Therefore, we provide setup ioctls, which allow user-space to pass in +parameters and options how the to-be-created object should behave. Once +setup is done, we allow user-space to use ioctls to operate on that newly +created object. + +It is important to notice: + 1) Only one setup ioctl can ever be called on a handle. You cannot call + multiple, different setup ioctls on the same handle. + 2) A setup ioctl can only be called once, if it succeeded. If it failed, + it must not modify the handle in any way. If it succeeded, no further + setup ioctl can be issued. + 3) After a setup ioctl is done, the handle is constant and must not be + modified in any way. + +So far, we used a write-lock around all setup ioctls, and a read-lock +around everything else. The handle setup-indicator (the type field) can +only be set under the write-lock. Whenever you access the handle under a +read-lock, you must verify it was set before, otherwise, you must bail out +as the handle was not initialized, yet. + +This has the downside that we need a read-lock on all operations on the +handle. For performance reasons, we should avoid that. This patch turns +the rwlock into a mutex and removes the read-side lock from all paths. It +relies on the 3 behaviors described above. + +With this patch, the mutex is only taken around setup ioctls. Furthermore, +the setup-indicator (the type field) is only ever set if the mutex is +held. The mutex guarantees that multiple setup ioctls cannot race, and +also, that only one setup ioctl will ever succeed. If a setup ioctl is +called after setup was already finished, we do not touch the handle at all +and immediately fail. + +Furthermore, all other operations (non-setup operations) can only be +called once setup is done. Therefore, we must synchronize them with any +racing setup, otherwise, they might access the handle which is currently +modified by setup. +We protect from this race by setting the setup-indicator (the type field) +_last_, and issue a write-barrier before setting it. Once it is set, we +never modify the handle ever again; it is constant from now on until +file-release. +Hence, on the read-side we simply read the type field and issue a +read-barrier afterwards. _Iff_ the type field was not set, yet, we must +not access the handle in any way, but bail out immediately. Setup was not +done, yet. But if the type field was set, the read-barrier pairs with the +write-barrier during setup. All member fields of the handle object are +guaranteed to be accessible by us, as the type-field is always the last +field that is written. + +With this in place, we reduce the locking-overhead of all non-setup ioctls +to a read-barrier, instead of a read-side lock. And in combination with +the follow-up that removes the active-refs from kdbus_handle_poll(), we're +now lock-free in ->poll and ->mmap callbacks. + +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Acked-by: Daniel Mack <daniel@zonque.org> +--- + ipc/kdbus/handle.c | 110 ++++++++++++++++++++++++++++++++++++++++------------- + 1 file changed, 83 insertions(+), 27 deletions(-) + +diff --git a/ipc/kdbus/handle.c b/ipc/kdbus/handle.c +index 3f5d8085a297..a3e01383a6f6 100644 +--- a/ipc/kdbus/handle.c ++++ b/ipc/kdbus/handle.c +@@ -18,6 +18,7 @@ + #include <linux/init.h> + #include <linux/kdev_t.h> + #include <linux/module.h> ++#include <linux/mutex.h> + #include <linux/poll.h> + #include <linux/rwsem.h> + #include <linux/sched.h> +@@ -229,7 +230,7 @@ enum kdbus_handle_type { + + /** + * struct kdbus_handle - handle to the kdbus system +- * @rwlock: handle lock ++ * @lock: handle lock + * @type: type of this handle (KDBUS_HANDLE_*) + * @bus_owner: bus this handle owns + * @ep_owner: endpoint this handle owns +@@ -237,7 +238,7 @@ enum kdbus_handle_type { + * @privileged: Flag to mark a handle as privileged + */ + struct kdbus_handle { +- struct rw_semaphore rwlock; ++ struct mutex lock; + + enum kdbus_handle_type type; + union { +@@ -265,7 +266,7 @@ static int kdbus_handle_open(struct inode *inode, struct file *file) + goto exit; + } + +- init_rwsem(&handle->rwlock); ++ mutex_init(&handle->lock); + handle->type = KDBUS_HANDLE_NONE; + + if (node->type == KDBUS_NODE_ENDPOINT) { +@@ -355,8 +356,8 @@ static long kdbus_handle_ioctl_control(struct file *file, unsigned int cmd, + break; + } + +- handle->type = KDBUS_HANDLE_BUS_OWNER; + handle->bus_owner = bus; ++ ret = KDBUS_HANDLE_BUS_OWNER; + break; + } + +@@ -396,8 +397,8 @@ static long kdbus_handle_ioctl_ep(struct file *file, unsigned int cmd, + break; + } + +- handle->type = KDBUS_HANDLE_EP_OWNER; + handle->ep_owner = ep; ++ ret = KDBUS_HANDLE_EP_OWNER; + break; + + case KDBUS_CMD_HELLO: +@@ -407,8 +408,8 @@ static long kdbus_handle_ioctl_ep(struct file *file, unsigned int cmd, + break; + } + +- handle->type = KDBUS_HANDLE_CONNECTED; + handle->conn = conn; ++ ret = KDBUS_HANDLE_CONNECTED; + break; + + default: +@@ -522,19 +523,41 @@ static long kdbus_handle_ioctl(struct file *file, unsigned int cmd, + case KDBUS_CMD_BUS_MAKE: + case KDBUS_CMD_ENDPOINT_MAKE: + case KDBUS_CMD_HELLO: +- /* bail out early if already typed */ +- if (handle->type != KDBUS_HANDLE_NONE) +- break; +- +- down_write(&handle->rwlock); ++ mutex_lock(&handle->lock); + if (handle->type == KDBUS_HANDLE_NONE) { + if (node->type == KDBUS_NODE_CONTROL) + ret = kdbus_handle_ioctl_control(file, cmd, + argp); + else if (node->type == KDBUS_NODE_ENDPOINT) + ret = kdbus_handle_ioctl_ep(file, cmd, argp); ++ ++ if (ret > 0) { ++ /* ++ * The data given via open() is not sufficient ++ * to setup a kdbus handle. Hence, we require ++ * the user to perform a setup ioctl. This setup ++ * can only be performed once and defines the ++ * type of the handle. The different setup ++ * ioctls are locked against each other so they ++ * cannot race. Once the handle type is set, ++ * the type-dependent ioctls are enabled. To ++ * improve performance, we don't lock those via ++ * handle->lock. Instead, we issue a ++ * write-barrier before performing the ++ * type-change, which pairs with smp_rmb() in ++ * all handlers that access the type field. This ++ * guarantees the handle is fully setup, if ++ * handle->type is set. If handle->type is ++ * unset, you must not make any assumptions ++ * without taking handle->lock. ++ * Note that handle->type is only set once. It ++ * will never change afterwards. ++ */ ++ smp_wmb(); ++ handle->type = ret; ++ } + } +- up_write(&handle->rwlock); ++ mutex_unlock(&handle->lock); + break; + + case KDBUS_CMD_ENDPOINT_UPDATE: +@@ -549,14 +572,30 @@ static long kdbus_handle_ioctl(struct file *file, unsigned int cmd, + case KDBUS_CMD_MATCH_REMOVE: + case KDBUS_CMD_SEND: + case KDBUS_CMD_RECV: +- case KDBUS_CMD_FREE: +- down_read(&handle->rwlock); +- if (handle->type == KDBUS_HANDLE_EP_OWNER) ++ case KDBUS_CMD_FREE: { ++ enum kdbus_handle_type type; ++ ++ /* ++ * This read-barrier pairs with smp_wmb() of the handle setup. ++ * it guarantees the handle is fully written, in case the ++ * type has been set. It allows us to access the handle without ++ * taking handle->lock, given the guarantee that the type is ++ * only ever set once, and stays constant afterwards. ++ * Furthermore, the handle object itself is not modified in any ++ * way after the type is set. That is, the type-field is the ++ * last field that is written on any handle. If it has not been ++ * set, we must not access the handle here. ++ */ ++ type = handle->type; ++ smp_rmb(); ++ ++ if (type == KDBUS_HANDLE_EP_OWNER) + ret = kdbus_handle_ioctl_ep_owner(file, cmd, argp); +- else if (handle->type == KDBUS_HANDLE_CONNECTED) ++ else if (type == KDBUS_HANDLE_CONNECTED) + ret = kdbus_handle_ioctl_connected(file, cmd, argp); +- up_read(&handle->rwlock); ++ + break; ++ } + default: + ret = -ENOTTY; + break; +@@ -569,16 +608,23 @@ static unsigned int kdbus_handle_poll(struct file *file, + struct poll_table_struct *wait) + { + struct kdbus_handle *handle = file->private_data; ++ enum kdbus_handle_type type; + unsigned int mask = POLLOUT | POLLWRNORM; + int ret; + ++ /* ++ * This pairs with smp_wmb() during handle setup. It guarantees that ++ * _iff_ the handle type is set, handle->conn is valid. Furthermore, ++ * _iff_ the type is set, the handle object is constant and never ++ * changed again. If it's not set, we must not access the handle but ++ * bail out. We also must assume no setup has taken place, yet. ++ */ ++ type = handle->type; ++ smp_rmb(); ++ + /* Only a connected endpoint can read/write data */ +- down_read(&handle->rwlock); +- if (handle->type != KDBUS_HANDLE_CONNECTED) { +- up_read(&handle->rwlock); ++ if (type != KDBUS_HANDLE_CONNECTED) + return POLLERR | POLLHUP; +- } +- up_read(&handle->rwlock); + + ret = kdbus_conn_acquire(handle->conn); + if (ret < 0) +@@ -598,13 +644,23 @@ static unsigned int kdbus_handle_poll(struct file *file, + static int kdbus_handle_mmap(struct file *file, struct vm_area_struct *vma) + { + struct kdbus_handle *handle = file->private_data; ++ enum kdbus_handle_type type; + int ret = -EBADFD; + +- if (down_read_trylock(&handle->rwlock)) { +- if (handle->type == KDBUS_HANDLE_CONNECTED) +- ret = kdbus_pool_mmap(handle->conn->pool, vma); +- up_read(&handle->rwlock); +- } ++ /* ++ * This pairs with smp_wmb() during handle setup. It guarantees that ++ * _iff_ the handle type is set, handle->conn is valid. Furthermore, ++ * _iff_ the type is set, the handle object is constant and never ++ * changed again. If it's not set, we must not access the handle but ++ * bail out. We also must assume no setup has taken place, yet. ++ */ ++ type = handle->type; ++ smp_rmb(); ++ ++ /* Only connected handles have a pool we can map */ ++ if (type == KDBUS_HANDLE_CONNECTED) ++ ret = kdbus_pool_mmap(handle->conn->pool, vma); ++ + return ret; + } + diff --git a/kdbus-remove-redundant-code-from-kdbus_conn_entry_ma.patch b/kdbus-remove-redundant-code-from-kdbus_conn_entry_ma.patch new file mode 100644 index 000000000..c31ad0644 --- /dev/null +++ b/kdbus-remove-redundant-code-from-kdbus_conn_entry_ma.patch @@ -0,0 +1,40 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Tue, 2 Jun 2015 18:48:48 +0300 +Subject: [PATCH] kdbus: remove redundant code from kdbus_conn_entry_make() + +We don't need to check `entry' for error, as in either case it is +returned as is. Return result of kdbus_queue_entry_new() directly. + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + ipc/kdbus/connection.c | 8 +------- + 1 file changed, 1 insertion(+), 7 deletions(-) + +diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c +index 8ee62fc0bd46..1bd7bb968f9f 100644 +--- a/ipc/kdbus/connection.c ++++ b/ipc/kdbus/connection.c +@@ -775,8 +775,6 @@ kdbus_conn_entry_make(struct kdbus_conn *conn_dst, + const struct kdbus_kmsg *kmsg, + struct kdbus_user *user) + { +- struct kdbus_queue_entry *entry; +- + /* The remote connection was disconnected */ + if (!kdbus_conn_active(conn_dst)) + return ERR_PTR(-ECONNRESET); +@@ -793,11 +791,7 @@ kdbus_conn_entry_make(struct kdbus_conn *conn_dst, + kmsg->res && kmsg->res->fds_count > 0) + return ERR_PTR(-ECOMM); + +- entry = kdbus_queue_entry_new(conn_dst, kmsg, user); +- if (IS_ERR(entry)) +- return entry; +- +- return entry; ++ return kdbus_queue_entry_new(conn_dst, kmsg, user); + } + + /* diff --git a/kdbus-remove-unused-linux-version.h-include.patch b/kdbus-remove-unused-linux-version.h-include.patch new file mode 100644 index 000000000..06e4807be --- /dev/null +++ b/kdbus-remove-unused-linux-version.h-include.patch @@ -0,0 +1,25 @@ +From: Wei Yongjun <yongjun_wei@trendmicro.com.cn> +Date: Thu, 16 Apr 2015 21:07:18 +0800 +Subject: [PATCH] kdbus: remove unused linux/version.h include + +Remove <linux/version.h> include, it's not needed. + +Signed-off-by: Wei Yongjun <yongjun_wei@trendmicro.com.cn> +Acked-by: Daniel Mack <daniel@zonque.org> +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +--- + ipc/kdbus/metadata.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c +index 3adc6c2c2e76..eeebfef11552 100644 +--- a/ipc/kdbus/metadata.c ++++ b/ipc/kdbus/metadata.c +@@ -29,7 +29,6 @@ + #include <linux/uidgid.h> + #include <linux/uio.h> + #include <linux/user_namespace.h> +-#include <linux/version.h> + + #include "bus.h" + #include "connection.h" diff --git a/kdbus-samples-kdbus-add-lrt.patch b/kdbus-samples-kdbus-add-lrt.patch new file mode 100644 index 000000000..7451e3f13 --- /dev/null +++ b/kdbus-samples-kdbus-add-lrt.patch @@ -0,0 +1,23 @@ +From: David Herrmann <dh.herrmann@gmail.com> +Date: Thu, 12 Mar 2015 17:27:31 +0100 +Subject: [PATCH] kdbus: samples/kdbus: add -lrt + +On older systems -lrt is needed for clock_gettime(). Add it to +HOSTLOADLIBES of kdbus-workers so it builds fine on those systems. + +Reported-by: Sasha Levin <sasha.levin@oracle.com> +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + samples/kdbus/Makefile | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/samples/kdbus/Makefile b/samples/kdbus/Makefile +index d009025369f4..eee9b9aed632 100644 +--- a/samples/kdbus/Makefile ++++ b/samples/kdbus/Makefile +@@ -8,3 +8,4 @@ always := $(hostprogs-y) + HOSTCFLAGS_kdbus-workers.o += \ + -I$(objtree)/usr/include/ \ + -I$(objtree)/include/uapi/ ++HOSTLOADLIBES_kdbus-workers := -lrt diff --git a/kdbus-selftests-add-build-dependencies-on-headers.patch b/kdbus-selftests-add-build-dependencies-on-headers.patch new file mode 100644 index 000000000..6f18a0134 --- /dev/null +++ b/kdbus-selftests-add-build-dependencies-on-headers.patch @@ -0,0 +1,24 @@ +From: David Herrmann <dh.herrmann@gmail.com> +Date: Wed, 3 Jun 2015 17:53:29 +0200 +Subject: [PATCH] kdbus/selftests: add build-dependencies on headers + +Make sure the selftests are re-built if one of the local headers changes. + +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +--- + tools/testing/selftests/kdbus/Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/testing/selftests/kdbus/Makefile b/tools/testing/selftests/kdbus/Makefile +index 076f9f40566d..7ad587b3c767 100644 +--- a/tools/testing/selftests/kdbus/Makefile ++++ b/tools/testing/selftests/kdbus/Makefile +@@ -34,7 +34,7 @@ all: kdbus-test + + include ../lib.mk + +-%.o: %.c ++%.o: %.c kdbus-enum.h kdbus-test.h kdbus-util.h + $(CC) $(CFLAGS) -c $< -o $@ + + kdbus-test: $(OBJS) diff --git a/kdbus-skip-acquiring-an-active-reference-in-poll.patch b/kdbus-skip-acquiring-an-active-reference-in-poll.patch new file mode 100644 index 000000000..afa67633a --- /dev/null +++ b/kdbus-skip-acquiring-an-active-reference-in-poll.patch @@ -0,0 +1,63 @@ +From: David Herrmann <dh.herrmann@gmail.com> +Date: Sat, 18 Apr 2015 13:04:42 +0200 +Subject: [PATCH] kdbus: skip acquiring an active reference in poll() + +During poll(), we currently acquire an active reference to the connection +in question to verify it's still active. If it's not active, anymore, we +return POLLHUP. + +This works fine, but requires an atomic_inc() to acquire the active +reference. However, all we need is a guarantee that the connection is +active right now, and a guarantee we're called again once this changes. +This is as simple as adding the waitqueue first, then checking the +active-state afterwards. kdbus_conn_disconnect() guarantees to wake us up +_after_ deactivating the connection, thus providing the required barrier +implicitly (in case someone is actually polling / waiting on the +connection). + +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Acked-by: Daniel Mack <daniel@zonque.org> +--- + ipc/kdbus/handle.c | 16 +++++++++------- + 1 file changed, 9 insertions(+), 7 deletions(-) + +diff --git a/ipc/kdbus/handle.c b/ipc/kdbus/handle.c +index a3e01383a6f6..6230c7ef4347 100644 +--- a/ipc/kdbus/handle.c ++++ b/ipc/kdbus/handle.c +@@ -610,7 +610,6 @@ static unsigned int kdbus_handle_poll(struct file *file, + struct kdbus_handle *handle = file->private_data; + enum kdbus_handle_type type; + unsigned int mask = POLLOUT | POLLWRNORM; +- int ret; + + /* + * This pairs with smp_wmb() during handle setup. It guarantees that +@@ -626,18 +625,21 @@ static unsigned int kdbus_handle_poll(struct file *file, + if (type != KDBUS_HANDLE_CONNECTED) + return POLLERR | POLLHUP; + +- ret = kdbus_conn_acquire(handle->conn); +- if (ret < 0) +- return POLLERR | POLLHUP; +- + poll_wait(file, &handle->conn->wait, wait); + ++ /* ++ * Verify the connection hasn't been deactivated _after_ adding the ++ * wait-queue. This guarantees, that if the connection is deactivated ++ * after we checked it, the waitqueue is signaled and we're called ++ * again. ++ */ ++ if (!kdbus_conn_active(handle->conn)) ++ return POLLERR | POLLHUP; ++ + if (!list_empty(&handle->conn->queue.msg_list) || + atomic_read(&handle->conn->lost_count) > 0) + mask |= POLLIN | POLLRDNORM; + +- kdbus_conn_release(handle->conn); +- + return mask; + } + diff --git a/kdbus-skip-mandatory-items-on-negotiation.patch b/kdbus-skip-mandatory-items-on-negotiation.patch new file mode 100644 index 000000000..a5f1cf83f --- /dev/null +++ b/kdbus-skip-mandatory-items-on-negotiation.patch @@ -0,0 +1,55 @@ +From: David Herrmann <dh.herrmann@gmail.com> +Date: Thu, 21 May 2015 20:03:29 +0200 +Subject: [PATCH] kdbus: skip mandatory items on negotiation + +The kdbus negotiation is used to figure out what items and flags an ioctl +supports. It is highly impractical to pass in mandatory items when all we +do is negotiation. Therefore, allow user-space to skip mandatory items if +KDBUS_FLAG_NEGOTIATE is passed. + +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Acked-by: Daniel Mack <daniel@zonque.org> +--- + ipc/kdbus/handle.c | 15 ++++++++++----- + 1 file changed, 10 insertions(+), 5 deletions(-) + +diff --git a/ipc/kdbus/handle.c b/ipc/kdbus/handle.c +index f72dbe513b4a..3f5d8085a297 100644 +--- a/ipc/kdbus/handle.c ++++ b/ipc/kdbus/handle.c +@@ -71,10 +71,6 @@ static int kdbus_args_verify(struct kdbus_args *args) + if (!KDBUS_ITEMS_END(item, args->items, args->items_size)) + return -EINVAL; + +- for (i = 0; i < args->argc; ++i) +- if (args->argv[i].mandatory && !args->argv[i].item) +- return -EINVAL; +- + return 0; + } + +@@ -149,7 +145,7 @@ static int kdbus_args_negotiate(struct kdbus_args *args) + int __kdbus_args_parse(struct kdbus_args *args, void __user *argp, + size_t type_size, size_t items_offset, void **out) + { +- int ret; ++ int ret, i; + + args->cmd = kdbus_memdup_user(argp, type_size, KDBUS_CMD_MAX_SIZE); + if (IS_ERR(args->cmd)) +@@ -173,6 +169,15 @@ int __kdbus_args_parse(struct kdbus_args *args, void __user *argp, + if (ret < 0) + goto error; + ++ /* mandatory items must be given (but not on negotiation) */ ++ if (!(args->cmd->flags & KDBUS_FLAG_NEGOTIATE)) { ++ for (i = 0; i < args->argc; ++i) ++ if (args->argv[i].mandatory && !args->argv[i].item) { ++ ret = -EINVAL; ++ goto error; ++ } ++ } ++ + *out = args->cmd; + return !!(args->cmd->flags & KDBUS_FLAG_NEGOTIATE); + diff --git a/kdbus-translate-capabilities-between-namespaces.patch b/kdbus-translate-capabilities-between-namespaces.patch new file mode 100644 index 000000000..b0cd8a4d4 --- /dev/null +++ b/kdbus-translate-capabilities-between-namespaces.patch @@ -0,0 +1,212 @@ +From: David Herrmann <dh.herrmann@gmail.com> +Date: Wed, 22 Apr 2015 13:14:24 +0200 +Subject: [PATCH] kdbus: translate capabilities between namespaces + +Right now, we always drop capability-items if we cross user-namespaces. +However, the kernel _does_ support capability translation, as defined in +./security/commoncap.c cap_capable(). + +This patch adds capability translation support just like cap_capable() +does. This way, a message sent from a task into a child user-namespace of +its own, will retain the capability-item and thus keep the parent +privileged inside of the user-namespace of its children. + +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +--- + ipc/kdbus/metadata.c | 126 ++++++++++++++++++++++++++++++++++----------------- + 1 file changed, 84 insertions(+), 42 deletions(-) + +diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c +index b908b6314a00..7949c8d3ed64 100644 +--- a/ipc/kdbus/metadata.c ++++ b/ipc/kdbus/metadata.c +@@ -63,8 +63,7 @@ + * @root_path: Root-FS path + * @cmdline: Command-line + * @cgroup: Full cgroup path +- * @caps: Capabilities +- * @caps_namespace: User-namespace of @caps ++ * @cred: Credentials + * @seclabel: Seclabel + * @audit_loginuid: Audit login-UID + * @audit_sessionid: Audit session-ID +@@ -104,14 +103,7 @@ struct kdbus_meta_proc { + char *cgroup; + + /* KDBUS_ITEM_CAPS */ +- struct caps { +- /* binary compatible to kdbus_caps */ +- u32 last_cap; +- struct { +- u32 caps[_KERNEL_CAPABILITY_U32S]; +- } set[4]; +- } caps; +- struct user_namespace *caps_namespace; ++ const struct cred *cred; + + /* KDBUS_ITEM_SECLABEL */ + char *seclabel; +@@ -149,6 +141,14 @@ struct kdbus_meta_conn { + char *conn_description; + }; + ++/* fixed size equivalent of "kdbus_caps" */ ++struct kdbus_meta_caps { ++ u32 last_cap; ++ struct { ++ u32 caps[_KERNEL_CAPABILITY_U32S]; ++ } set[4]; ++}; ++ + /** + * kdbus_meta_proc_new() - Create process metadata object + * +@@ -175,7 +175,8 @@ static void kdbus_meta_proc_free(struct kref *kref) + + path_put(&mp->exe_path); + path_put(&mp->root_path); +- put_user_ns(mp->caps_namespace); ++ if (mp->cred) ++ put_cred(mp->cred); + put_pid(mp->ppid); + put_pid(mp->tgid); + put_pid(mp->pid); +@@ -354,25 +355,7 @@ static int kdbus_meta_proc_collect_cgroup(struct kdbus_meta_proc *mp) + + static void kdbus_meta_proc_collect_caps(struct kdbus_meta_proc *mp) + { +- const struct cred *c = current_cred(); +- int i; +- +- /* ABI: "last_cap" equals /proc/sys/kernel/cap_last_cap */ +- mp->caps.last_cap = CAP_LAST_CAP; +- mp->caps_namespace = get_user_ns(current_user_ns()); +- +- CAP_FOR_EACH_U32(i) { +- mp->caps.set[0].caps[i] = c->cap_inheritable.cap[i]; +- mp->caps.set[1].caps[i] = c->cap_permitted.cap[i]; +- mp->caps.set[2].caps[i] = c->cap_effective.cap[i]; +- mp->caps.set[3].caps[i] = c->cap_bset.cap[i]; +- } +- +- /* clear unused bits */ +- for (i = 0; i < 4; i++) +- mp->caps.set[i].caps[CAP_TO_INDEX(CAP_LAST_CAP)] &= +- CAP_LAST_U32_VALID_MASK; +- ++ mp->cred = get_current_cred(); + mp->valid |= KDBUS_ATTACH_CAPS; + } + +@@ -880,7 +863,7 @@ int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp, + size += KDBUS_ITEM_SIZE(strlen(mp->cgroup) + 1); + + if (mp && (*mask & KDBUS_ATTACH_CAPS)) +- size += KDBUS_ITEM_SIZE(sizeof(mp->caps)); ++ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_meta_caps)); + + if (mp && (*mask & KDBUS_ATTACH_SECLABEL)) + size += KDBUS_ITEM_SIZE(strlen(mp->seclabel) + 1); +@@ -917,6 +900,69 @@ static int kdbus_meta_push_kvec(struct kvec *kvec, + return 2 + !!kdbus_kvec_pad(kvec++, size); + } + ++static void kdbus_meta_export_caps(struct kdbus_meta_caps *out, ++ struct kdbus_meta_proc *mp) ++{ ++ struct user_namespace *iter; ++ const struct cred *cred = mp->cred; ++ bool parent = false, owner = false; ++ int i; ++ ++ /* ++ * This translates the effective capabilities of 'cred' into the current ++ * user-namespace. If the current user-namespace is a child-namespace of ++ * the user-namespace of 'cred', the mask can be copied verbatim. If ++ * not, the mask is cleared. ++ * There's one exception: If 'cred' is the owner of any user-namespace ++ * in the path between the current user-namespace and the user-namespace ++ * of 'cred', then it has all effective capabilities set. This means, ++ * the user who created a user-namespace always has all effective ++ * capabilities in any child namespaces. Note that this is based on the ++ * uid of the namespace creator, not the task hierarchy. ++ */ ++ for (iter = current_user_ns(); iter; iter = iter->parent) { ++ if (iter == cred->user_ns) { ++ parent = true; ++ break; ++ } ++ ++ if (iter == &init_user_ns) ++ break; ++ ++ if ((iter->parent == cred->user_ns) && ++ uid_eq(iter->owner, cred->euid)) { ++ owner = true; ++ break; ++ } ++ } ++ ++ out->last_cap = CAP_LAST_CAP; ++ ++ CAP_FOR_EACH_U32(i) { ++ if (parent) { ++ out->set[0].caps[i] = cred->cap_inheritable.cap[i]; ++ out->set[1].caps[i] = cred->cap_permitted.cap[i]; ++ out->set[2].caps[i] = cred->cap_effective.cap[i]; ++ out->set[3].caps[i] = cred->cap_bset.cap[i]; ++ } else if (owner) { ++ out->set[0].caps[i] = 0U; ++ out->set[1].caps[i] = ~0U; ++ out->set[2].caps[i] = ~0U; ++ out->set[3].caps[i] = ~0U; ++ } else { ++ out->set[0].caps[i] = 0U; ++ out->set[1].caps[i] = 0U; ++ out->set[2].caps[i] = 0U; ++ out->set[3].caps[i] = 0U; ++ } ++ } ++ ++ /* clear unused bits */ ++ for (i = 0; i < 4; i++) ++ out->set[i].caps[CAP_TO_INDEX(CAP_LAST_CAP)] &= ++ CAP_LAST_U32_VALID_MASK; ++} ++ + /* This is equivalent to from_kuid_munged(), but maps INVALID_UID to itself */ + static uid_t kdbus_from_kuid_keep(kuid_t uid) + { +@@ -975,14 +1021,6 @@ int kdbus_meta_export(struct kdbus_meta_proc *mp, + + hdr = &item_hdr[0]; + +- /* +- * TODO: We currently have no sane way of translating a set of caps +- * between different user namespaces. Until that changes, we have +- * to drop such items. +- */ +- if (mp && mp->caps_namespace != user_ns) +- mask &= ~KDBUS_ATTACH_CAPS; +- + if (mask == 0) { + *real_size = 0; + return 0; +@@ -1088,10 +1126,14 @@ int kdbus_meta_export(struct kdbus_meta_proc *mp, + KDBUS_ITEM_CGROUP, mp->cgroup, + strlen(mp->cgroup) + 1, &size); + +- if (mp && (mask & KDBUS_ATTACH_CAPS)) ++ if (mp && (mask & KDBUS_ATTACH_CAPS)) { ++ struct kdbus_meta_caps caps = {}; ++ ++ kdbus_meta_export_caps(&caps, mp); + cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, +- KDBUS_ITEM_CAPS, &mp->caps, +- sizeof(mp->caps), &size); ++ KDBUS_ITEM_CAPS, &caps, ++ sizeof(caps), &size); ++ } + + if (mp && (mask & KDBUS_ATTACH_SECLABEL)) + cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, diff --git a/kdbus-turn-kdbus_node_idr-into-an-ida.patch b/kdbus-turn-kdbus_node_idr-into-an-ida.patch new file mode 100644 index 000000000..e6bd2f339 --- /dev/null +++ b/kdbus-turn-kdbus_node_idr-into-an-ida.patch @@ -0,0 +1,101 @@ +From: David Herrmann <dh.herrmann@gmail.com> +Date: Sat, 18 Apr 2015 12:00:33 +0200 +Subject: [PATCH] kdbus: turn kdbus_node_idr into an ida + +We no longer use the node-idr for lookups. We're only interested in unique +ID allocation. Hence, turn the kdbus_node_idr into an ida and drop the now +redundant locking. This is also what kernfs does for ino allocations. + +Reported-by: Al Viro <viro@zeniv.linux.org.uk> +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Acked-by: Daniel Mack <daniel@zonque.org> +--- + ipc/kdbus/main.c | 1 + + ipc/kdbus/node.c | 23 +++++------------------ + ipc/kdbus/node.h | 2 ++ + 3 files changed, 8 insertions(+), 18 deletions(-) + +diff --git a/ipc/kdbus/main.c b/ipc/kdbus/main.c +index 785f529d98b7..f8eac78cace6 100644 +--- a/ipc/kdbus/main.c ++++ b/ipc/kdbus/main.c +@@ -116,6 +116,7 @@ static void __exit kdbus_exit(void) + { + kdbus_fs_exit(); + kobject_put(kdbus_dir); ++ ida_destroy(&kdbus_node_ida); + } + + module_init(kdbus_init); +diff --git a/ipc/kdbus/node.c b/ipc/kdbus/node.c +index 520df00e676a..0d65c65d2bde 100644 +--- a/ipc/kdbus/node.c ++++ b/ipc/kdbus/node.c +@@ -178,7 +178,7 @@ + * accessed by other callers to properly initialize + * filesystem nodes. + * +- * * node->id: This is an unsigned 32bit integer allocated by an IDR. It is ++ * * node->id: This is an unsigned 32bit integer allocated by an IDA. It is + * always kept as small as possible during allocation and is + * globally unique across all nodes allocated by this module. 0 + * is reserved as "not assigned" and is the default. +@@ -233,8 +233,7 @@ + #define KDBUS_NODE_NEW (KDBUS_NODE_BIAS - 4) + + /* global unique ID mapping for kdbus nodes */ +-static DEFINE_IDR(kdbus_node_idr); +-static DECLARE_RWSEM(kdbus_node_idr_lock); ++DEFINE_IDA(kdbus_node_ida); + + /** + * kdbus_node_name_hash() - hash a name +@@ -337,15 +336,11 @@ int kdbus_node_link(struct kdbus_node *node, struct kdbus_node *parent, + node->hash = kdbus_node_name_hash(name); + } + +- down_write(&kdbus_node_idr_lock); +- ret = idr_alloc(&kdbus_node_idr, node, 1, 0, GFP_KERNEL); +- if (ret >= 0) +- node->id = ret; +- up_write(&kdbus_node_idr_lock); +- ++ ret = ida_simple_get(&kdbus_node_ida, 1, 0, GFP_KERNEL); + if (ret < 0) + return ret; + ++ node->id = ret; + ret = 0; + + if (parent) { +@@ -440,16 +435,8 @@ struct kdbus_node *kdbus_node_unref(struct kdbus_node *node) + + if (node->free_cb) + node->free_cb(node); +- +- down_write(&kdbus_node_idr_lock); + if (safe.id > 0) +- idr_remove(&kdbus_node_idr, safe.id); +- /* drop caches after last node to not leak memory on unload */ +- if (idr_is_empty(&kdbus_node_idr)) { +- idr_destroy(&kdbus_node_idr); +- idr_init(&kdbus_node_idr); +- } +- up_write(&kdbus_node_idr_lock); ++ ida_simple_remove(&kdbus_node_ida, safe.id); + + kfree(safe.name); + +diff --git a/ipc/kdbus/node.h b/ipc/kdbus/node.h +index be125ce4fd58..970e02b08e9f 100644 +--- a/ipc/kdbus/node.h ++++ b/ipc/kdbus/node.h +@@ -58,6 +58,8 @@ struct kdbus_node { + + #define kdbus_node_from_rb(_node) rb_entry((_node), struct kdbus_node, rb) + ++extern struct ida kdbus_node_ida; ++ + void kdbus_node_init(struct kdbus_node *node, unsigned int type); + + int kdbus_node_link(struct kdbus_node *node, struct kdbus_node *parent, diff --git a/kdbus-uapi-Fix-kernel-doc-for-enum-kdbus_send_flags.patch b/kdbus-uapi-Fix-kernel-doc-for-enum-kdbus_send_flags.patch new file mode 100644 index 000000000..5236343c3 --- /dev/null +++ b/kdbus-uapi-Fix-kernel-doc-for-enum-kdbus_send_flags.patch @@ -0,0 +1,24 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Thu, 9 Apr 2015 13:11:01 +0300 +Subject: [PATCH] kdbus: uapi: Fix kernel-doc for enum kdbus_send_flags + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Acked-by: Daniel Mack <daniel@zonque.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + include/uapi/linux/kdbus.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/uapi/linux/kdbus.h b/include/uapi/linux/kdbus.h +index 2fe0a1c5056c..00a6e142c977 100644 +--- a/include/uapi/linux/kdbus.h ++++ b/include/uapi/linux/kdbus.h +@@ -544,7 +544,7 @@ struct kdbus_msg_info { + * reply to this message. The + * KDBUS_CMD_SEND ioctl() will block + * until the reply is received, and +- * offset_reply in struct kdbus_msg will ++ * reply in struct kdbus_cmd_send will + * yield the offset in the sender's pool + * where the reply can be found. + * This flag is only valid if diff --git a/kdbus-update-kernel-doc-for-kdbus_sync_reply_wakeup.patch b/kdbus-update-kernel-doc-for-kdbus_sync_reply_wakeup.patch new file mode 100644 index 000000000..eca8a10a1 --- /dev/null +++ b/kdbus-update-kernel-doc-for-kdbus_sync_reply_wakeup.patch @@ -0,0 +1,28 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Tue, 2 Jun 2015 18:48:47 +0300 +Subject: [PATCH] kdbus: update kernel-doc for kdbus_sync_reply_wakeup() + +kdbus_sync_reply_wakeup() doesn't remove reply object from connection +reply_list. Update function description. + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + ipc/kdbus/reply.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/ipc/kdbus/reply.c b/ipc/kdbus/reply.c +index 008dca801627..89d355b44f63 100644 +--- a/ipc/kdbus/reply.c ++++ b/ipc/kdbus/reply.c +@@ -140,8 +140,7 @@ void kdbus_reply_unlink(struct kdbus_reply *r) + * @reply: The reply object + * @err: Error code to set on the remote side + * +- * Remove the synchronous reply object from its connection reply_list, and +- * wake up remote peer (method origin) with the appropriate synchronous reply ++ * Wake up remote peer (method origin) with the appropriate synchronous reply + * code. + */ + void kdbus_sync_reply_wakeup(struct kdbus_reply *reply, int err) diff --git a/kdbus-use-FIELD_SIZEOF-in-kdbus_member_set_user-macr.patch b/kdbus-use-FIELD_SIZEOF-in-kdbus_member_set_user-macr.patch new file mode 100644 index 000000000..bbf3b7ab1 --- /dev/null +++ b/kdbus-use-FIELD_SIZEOF-in-kdbus_member_set_user-macr.patch @@ -0,0 +1,29 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Thu, 4 Jun 2015 13:39:30 +0300 +Subject: [PATCH] kdbus: use FIELD_SIZEOF in kdbus_member_set_user macro + +sizeof(((_t *)0)->_m) -> FIELD_SIZEOF(_t, _m) + +Use conventional macro according to chapter 17 of +Documentation/CodingStyle. + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + ipc/kdbus/util.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipc/kdbus/util.h b/ipc/kdbus/util.h +index 9fedf8ab41cd..529716669fe7 100644 +--- a/ipc/kdbus/util.h ++++ b/ipc/kdbus/util.h +@@ -40,7 +40,7 @@ + ({ \ + u64 __user *_sz = \ + (void __user *)((u8 __user *)(_b) + offsetof(_t, _m)); \ +- copy_to_user(_sz, _s, sizeof(((_t *)0)->_m)); \ ++ copy_to_user(_sz, _s, FIELD_SIZEOF(_t, _m)); \ + }) + + /** diff --git a/kdbus-use-parentheses-uniformly-in-KDBUS_ITEMS_FOREA.patch b/kdbus-use-parentheses-uniformly-in-KDBUS_ITEMS_FOREA.patch new file mode 100644 index 000000000..bbf3c881d --- /dev/null +++ b/kdbus-use-parentheses-uniformly-in-KDBUS_ITEMS_FOREA.patch @@ -0,0 +1,31 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Wed, 10 Jun 2015 00:00:00 +0300 +Subject: [PATCH] kdbus: use parentheses uniformly in KDBUS_ITEMS_FOREACH macro + +Enclose all arguments into parentheses to stay consistent across the +whole macro. + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + ipc/kdbus/item.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/ipc/kdbus/item.h b/ipc/kdbus/item.h +index 32909e2e7954..bca63b4e6e80 100644 +--- a/ipc/kdbus/item.h ++++ b/ipc/kdbus/item.h +@@ -28,10 +28,10 @@ + #define KDBUS_ITEM_PAYLOAD_SIZE(_i) ((_i)->size - KDBUS_ITEM_HEADER_SIZE) + + #define KDBUS_ITEMS_FOREACH(_i, _is, _s) \ +- for (_i = _is; \ ++ for ((_i) = (_is); \ + ((u8 *)(_i) < (u8 *)(_is) + (_s)) && \ + ((u8 *)(_i) >= (u8 *)(_is)); \ +- _i = KDBUS_ITEM_NEXT(_i)) ++ (_i) = KDBUS_ITEM_NEXT(_i)) + + #define KDBUS_ITEM_VALID(_i, _is, _s) \ + ((_i)->size >= KDBUS_ITEM_HEADER_SIZE && \ diff --git a/kdbus-use-rcu-to-access-exe-file-in-metadata.patch b/kdbus-use-rcu-to-access-exe-file-in-metadata.patch new file mode 100644 index 000000000..19678786a --- /dev/null +++ b/kdbus-use-rcu-to-access-exe-file-in-metadata.patch @@ -0,0 +1,44 @@ +From: Daniel Mack <daniel@zonque.org> +Date: Sat, 18 Apr 2015 12:04:36 +0200 +Subject: [PATCH] kdbus: use rcu to access exe file in metadata + +Commit 90f31d0ea888 ("mm: rcu-protected get_mm_exe_file()") removed +mm->mmap_sem from mm->exe_file read side. Follow that change in the +kdbus metadata code. + +Signed-off-by: Daniel Mack <daniel@zonque.org> +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +--- + ipc/kdbus/metadata.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c +index 7949c8d3ed64..a85eac34a5c4 100644 +--- a/ipc/kdbus/metadata.c ++++ b/ipc/kdbus/metadata.c +@@ -283,19 +283,21 @@ static void kdbus_meta_proc_collect_pid_comm(struct kdbus_meta_proc *mp) + static void kdbus_meta_proc_collect_exe(struct kdbus_meta_proc *mp) + { + struct mm_struct *mm; ++ struct file *exe_file; + + mm = get_task_mm(current); + if (!mm) + return; + +- down_read(&mm->mmap_sem); +- if (mm->exe_file) { +- mp->exe_path = mm->exe_file->f_path; ++ rcu_read_lock(); ++ exe_file = rcu_dereference(mm->exe_file); ++ if (exe_file) { ++ mp->exe_path = exe_file->f_path; + path_get(&mp->exe_path); + get_fs_root(current->fs, &mp->root_path); + mp->valid |= KDBUS_ATTACH_EXE; + } +- up_read(&mm->mmap_sem); ++ rcu_read_unlock(); + + mmput(mm); + } diff --git a/kernel.spec b/kernel.spec index fe686cd85..74248b38c 100644 --- a/kernel.spec +++ b/kernel.spec @@ -67,7 +67,7 @@ Summary: The Linux kernel # The rc snapshot level %define rcrev 1 # The git snapshot level -%define gitrev 1 +%define gitrev 2 # Set rpm version accordingly %define rpmversion 4.%{upstream_sublevel}.0 %endif @@ -596,6 +596,162 @@ Patch502: firmware-Drop-WARN-from-usermodehelper_read_trylock-.patch Patch503: drm-i915-turn-off-wc-mmaps.patch +Patch504: kdbus-add-documentation.patch + +Patch505: kdbus-add-uapi-header-file.patch + +Patch506: kdbus-add-driver-skeleton-ioctl-entry-points-and-uti.patch + +Patch507: kdbus-add-connection-pool-implementation.patch + +Patch508: kdbus-add-connection-queue-handling-and-message-vali.patch + +Patch509: kdbus-add-node-and-filesystem-implementation.patch + +Patch510: kdbus-add-code-to-gather-metadata.patch + +Patch511: kdbus-add-code-for-notifications-and-matches.patch + +Patch512: kdbus-add-code-for-buses-domains-and-endpoints.patch + +Patch513: kdbus-add-name-registry-implementation.patch + +Patch514: kdbus-add-policy-database-implementation.patch + +Patch515: kdbus-add-Makefile-Kconfig-and-MAINTAINERS-entry.patch + +Patch516: kdbus-add-walk-through-user-space-example.patch + +Patch517: kdbus-add-selftests.patch + +Patch518: Documentation-kdbus-fix-location-for-generated-files.patch + +Patch519: kdbus-samples-kdbus-add-lrt.patch + +Patch520: kdbus-fix-minor-typo-in-the-walk-through-example.patch + +Patch521: samples-kdbus-drop-wrong-include.patch + +Patch522: Documentation-kdbus-fix-out-of-tree-builds.patch + +Patch523: Documentation-kdbus-support-quiet-builds.patch + +Patch524: selftests-kdbus-fix-gitignore.patch + +Patch525: Documentation-kdbus-replace-reply_cookie-with-cookie.patch + +Patch526: kdbus-fix-header-guard-name.patch + +Patch527: kdbus-connection-fix-handling-of-failed-fget.patch + +Patch528: kdbus-Fix-CONFIG_KDBUS-help-text.patch + +Patch529: samples-kdbus-build-kdbus-workers-conditionally.patch + +Patch530: selftest-kdbus-enable-cross-compilation.patch + +Patch531: kdbus-uapi-Fix-kernel-doc-for-enum-kdbus_send_flags.patch + +Patch532: Documentation-kdbus-Fix-list-of-KDBUS_CMD_ENDPOINT_U.patch + +Patch533: Documentation-kdbus-Update-list-of-ioctls-which-caus.patch + +Patch534: Documentation-kdbus-Fix-description-of-KDBUS_SEND_SY.patch + +Patch535: Documentation-kdbus-Fix-typos.patch + +Patch536: kdbus-avoid-the-use-of-struct-timespec.patch + +Patch537: kdbus-pool-use-__vfs_read.patch + +Patch538: kdbus-skip-mandatory-items-on-negotiation.patch + +Patch539: kdbus-turn-kdbus_node_idr-into-an-ida.patch + +Patch540: kdbus-reduce-scope-of-handle-locking.patch + +Patch541: kdbus-skip-acquiring-an-active-reference-in-poll.patch + +Patch542: kdbus-remove-unused-linux-version.h-include.patch + +Patch543: kdbus-optimize-auxgroup-collector.patch + +Patch544: kdbus-drop-obsolete-WARN_ON.patch + +Patch545: kdbus-copy-small-ioctl-payloads-to-stack.patch + +Patch546: kdbus-drop-kdbus_meta_attach_mask-modparam.patch + +Patch547: kdbus-fix-typo.patch + +Patch548: kdbus-forward-ID-notifications-to-everyone.patch + +Patch549: kdbus-provide-helper-to-collect-metadata.patch + +Patch550: kdbus-make-metadata-on-broadcasts-reliable.patch + +Patch551: samples-kdbus-stub-out-code-for-glibc-2.7.patch + +Patch552: kdbus-fix-up-documentation-of-ioctl-handlers.patch + +Patch553: kdbus-translate-capabilities-between-namespaces.patch + +Patch554: kdbus-selftests-add-build-dependencies-on-headers.patch + +Patch555: kdbus-use-rcu-to-access-exe-file-in-metadata.patch + +Patch556: kdbus-no-need-to-ref-current-mm.patch + +Patch557: selftests-kdbus-install-kdbus-test.patch + +Patch558: kdbus-update-kernel-doc-for-kdbus_sync_reply_wakeup.patch + +Patch559: kdbus-remove-redundant-code-from-kdbus_conn_entry_ma.patch + +Patch560: kdbus-kdbus_item_validate-remove-duplicated-code.patch + +Patch561: kdbus-kdbus_conn_connect-use-bus-instead-of-conn-ep-.patch + +Patch562: kdbus-use-FIELD_SIZEOF-in-kdbus_member_set_user-macr.patch + +Patch563: selftests-kdbus-handle-cap_get_proc-error-properly.patch + +Patch564: selftests-kdbus-drop-useless-assignment.patch + +Patch565: selftests-kdbus-remove-useless-initializations-from-.patch + +Patch566: selftests-kdbus-drop-duplicated-code-from-__kdbus_ms.patch + +Patch567: selftests-kdbus-fix-error-paths-in-__kdbus_msg_send.patch + +Patch568: kdbus-drop-useless-goto.patch + +Patch569: kdbus-fix-operator-precedence-issues-in-item-macros.patch + +Patch570: kdbus-use-parentheses-uniformly-in-KDBUS_ITEMS_FOREA.patch + +Patch571: Documentation-kdbus-fix-operator-precedence-issue-in.patch + +Patch572: Documentation-kdbus-use-parentheses-uniformly-in-KDB.patch + +Patch573: selftests-kdbus-fix-trivial-style-issues.patch + +Patch574: selftests-kdbus-fix-precedence-issues-in-macros.patch + +Patch575: selftests-kdbus-use-parentheses-in-iteration-macros-.patch + +Patch576: samples-kdbus-add-whitespace.patch + +Patch577: samples-kdbus-fix-operator-precedence-issue-in-KDBUS.patch + +Patch578: samples-kdbus-use-parentheses-uniformly-in-KDBUS_FOR.patch + +Patch579: kdbus-kdbus_reply_find-return-on-found-entry.patch + +Patch580: kdbus-optimize-error-path-in-kdbus_reply_new.patch + +Patch581: kdbus-optimize-if-statements-in-kdbus_conn_disconnec.patch + # END OF PATCH DEFINITIONS @@ -1266,6 +1422,162 @@ ApplyPatch firmware-Drop-WARN-from-usermodehelper_read_trylock-.patch ApplyPatch drm-i915-turn-off-wc-mmaps.patch +ApplyPatch kdbus-add-documentation.patch + +ApplyPatch kdbus-add-uapi-header-file.patch + +ApplyPatch kdbus-add-driver-skeleton-ioctl-entry-points-and-uti.patch + +ApplyPatch kdbus-add-connection-pool-implementation.patch + +ApplyPatch kdbus-add-connection-queue-handling-and-message-vali.patch + +ApplyPatch kdbus-add-node-and-filesystem-implementation.patch + +ApplyPatch kdbus-add-code-to-gather-metadata.patch + +ApplyPatch kdbus-add-code-for-notifications-and-matches.patch + +ApplyPatch kdbus-add-code-for-buses-domains-and-endpoints.patch + +ApplyPatch kdbus-add-name-registry-implementation.patch + +ApplyPatch kdbus-add-policy-database-implementation.patch + +ApplyPatch kdbus-add-Makefile-Kconfig-and-MAINTAINERS-entry.patch + +ApplyPatch kdbus-add-walk-through-user-space-example.patch + +ApplyPatch kdbus-add-selftests.patch + +ApplyPatch Documentation-kdbus-fix-location-for-generated-files.patch + +ApplyPatch kdbus-samples-kdbus-add-lrt.patch + +ApplyPatch kdbus-fix-minor-typo-in-the-walk-through-example.patch + +ApplyPatch samples-kdbus-drop-wrong-include.patch + +ApplyPatch Documentation-kdbus-fix-out-of-tree-builds.patch + +ApplyPatch Documentation-kdbus-support-quiet-builds.patch + +ApplyPatch selftests-kdbus-fix-gitignore.patch + +ApplyPatch Documentation-kdbus-replace-reply_cookie-with-cookie.patch + +ApplyPatch kdbus-fix-header-guard-name.patch + +ApplyPatch kdbus-connection-fix-handling-of-failed-fget.patch + +ApplyPatch kdbus-Fix-CONFIG_KDBUS-help-text.patch + +ApplyPatch samples-kdbus-build-kdbus-workers-conditionally.patch + +ApplyPatch selftest-kdbus-enable-cross-compilation.patch + +ApplyPatch kdbus-uapi-Fix-kernel-doc-for-enum-kdbus_send_flags.patch + +ApplyPatch Documentation-kdbus-Fix-list-of-KDBUS_CMD_ENDPOINT_U.patch + +ApplyPatch Documentation-kdbus-Update-list-of-ioctls-which-caus.patch + +ApplyPatch Documentation-kdbus-Fix-description-of-KDBUS_SEND_SY.patch + +ApplyPatch Documentation-kdbus-Fix-typos.patch + +ApplyPatch kdbus-avoid-the-use-of-struct-timespec.patch + +ApplyPatch kdbus-pool-use-__vfs_read.patch + +ApplyPatch kdbus-skip-mandatory-items-on-negotiation.patch + +ApplyPatch kdbus-turn-kdbus_node_idr-into-an-ida.patch + +ApplyPatch kdbus-reduce-scope-of-handle-locking.patch + +ApplyPatch kdbus-skip-acquiring-an-active-reference-in-poll.patch + +ApplyPatch kdbus-remove-unused-linux-version.h-include.patch + +ApplyPatch kdbus-optimize-auxgroup-collector.patch + +ApplyPatch kdbus-drop-obsolete-WARN_ON.patch + +ApplyPatch kdbus-copy-small-ioctl-payloads-to-stack.patch + +ApplyPatch kdbus-drop-kdbus_meta_attach_mask-modparam.patch + +ApplyPatch kdbus-fix-typo.patch + +ApplyPatch kdbus-forward-ID-notifications-to-everyone.patch + +ApplyPatch kdbus-provide-helper-to-collect-metadata.patch + +ApplyPatch kdbus-make-metadata-on-broadcasts-reliable.patch + +ApplyPatch samples-kdbus-stub-out-code-for-glibc-2.7.patch + +ApplyPatch kdbus-fix-up-documentation-of-ioctl-handlers.patch + +ApplyPatch kdbus-translate-capabilities-between-namespaces.patch + +ApplyPatch kdbus-selftests-add-build-dependencies-on-headers.patch + +ApplyPatch kdbus-use-rcu-to-access-exe-file-in-metadata.patch + +ApplyPatch kdbus-no-need-to-ref-current-mm.patch + +ApplyPatch selftests-kdbus-install-kdbus-test.patch + +ApplyPatch kdbus-update-kernel-doc-for-kdbus_sync_reply_wakeup.patch + +ApplyPatch kdbus-remove-redundant-code-from-kdbus_conn_entry_ma.patch + +ApplyPatch kdbus-kdbus_item_validate-remove-duplicated-code.patch + +ApplyPatch kdbus-kdbus_conn_connect-use-bus-instead-of-conn-ep-.patch + +ApplyPatch kdbus-use-FIELD_SIZEOF-in-kdbus_member_set_user-macr.patch + +ApplyPatch selftests-kdbus-handle-cap_get_proc-error-properly.patch + +ApplyPatch selftests-kdbus-drop-useless-assignment.patch + +ApplyPatch selftests-kdbus-remove-useless-initializations-from-.patch + +ApplyPatch selftests-kdbus-drop-duplicated-code-from-__kdbus_ms.patch + +ApplyPatch selftests-kdbus-fix-error-paths-in-__kdbus_msg_send.patch + +ApplyPatch kdbus-drop-useless-goto.patch + +ApplyPatch kdbus-fix-operator-precedence-issues-in-item-macros.patch + +ApplyPatch kdbus-use-parentheses-uniformly-in-KDBUS_ITEMS_FOREA.patch + +ApplyPatch Documentation-kdbus-fix-operator-precedence-issue-in.patch + +ApplyPatch Documentation-kdbus-use-parentheses-uniformly-in-KDB.patch + +ApplyPatch selftests-kdbus-fix-trivial-style-issues.patch + +ApplyPatch selftests-kdbus-fix-precedence-issues-in-macros.patch + +ApplyPatch selftests-kdbus-use-parentheses-in-iteration-macros-.patch + +ApplyPatch samples-kdbus-add-whitespace.patch + +ApplyPatch samples-kdbus-fix-operator-precedence-issue-in-KDBUS.patch + +ApplyPatch samples-kdbus-use-parentheses-uniformly-in-KDBUS_FOR.patch + +ApplyPatch kdbus-kdbus_reply_find-return-on-found-entry.patch + +ApplyPatch kdbus-optimize-error-path-in-kdbus_reply_new.patch + +ApplyPatch kdbus-optimize-if-statements-in-kdbus_conn_disconnec.patch + # END OF PATCH APPLICATIONS @@ -2131,6 +2443,12 @@ fi # # %changelog +* Wed Jul 08 2015 Josh Boyer <jwboyer@fedoraproject.org> - 4.2.0-0.rc1.git2.1 +- Linux v4.2-rc1-33-gd6ac4ffc61ac + +* Tue Jul 07 2015 Josh Boyer <jwboyer@fedoraproject.org> +- Add kdbus + * Tue Jul 07 2015 Josh Boyer <jwboyer@fedoraproject.org> - 4.2.0-0.rc1.git1.1 - Linux v4.2-rc1-17-gc7e9ad7da219 - Reenable debugging options. diff --git a/samples-kdbus-add-whitespace.patch b/samples-kdbus-add-whitespace.patch new file mode 100644 index 000000000..70516a842 --- /dev/null +++ b/samples-kdbus-add-whitespace.patch @@ -0,0 +1,24 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Wed, 10 Jun 2015 00:00:06 +0300 +Subject: [PATCH] samples/kdbus: add whitespace + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + samples/kdbus/kdbus-api.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/samples/kdbus/kdbus-api.h b/samples/kdbus/kdbus-api.h +index 5ed5907c5cb4..2de4d6a8c51e 100644 +--- a/samples/kdbus/kdbus-api.h ++++ b/samples/kdbus/kdbus-api.h +@@ -13,7 +13,7 @@ + for (iter = (first); \ + ((uint8_t *)(iter) < (uint8_t *)(first) + (_size)) && \ + ((uint8_t *)(iter) >= (uint8_t *)(first)); \ +- iter = (void*)(((uint8_t *)iter) + KDBUS_ALIGN8((iter)->size))) ++ iter = (void *)(((uint8_t *)iter) + KDBUS_ALIGN8((iter)->size))) + + static inline int kdbus_cmd_bus_make(int control_fd, struct kdbus_cmd *cmd) + { diff --git a/samples-kdbus-build-kdbus-workers-conditionally.patch b/samples-kdbus-build-kdbus-workers-conditionally.patch new file mode 100644 index 000000000..131e43b19 --- /dev/null +++ b/samples-kdbus-build-kdbus-workers-conditionally.patch @@ -0,0 +1,47 @@ +From: Daniel Mack <daniel@zonque.org> +Date: Tue, 31 Mar 2015 15:11:34 +0200 +Subject: [PATCH] samples: kdbus: build kdbus-workers conditionally + +Give the kdbus sample its own config switch and only build it if it's +explicitly switched on. + +Signed-off-by: Daniel Mack <daniel@zonque.org> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Reported-by: Jiri Slaby <jslaby@suse.cz> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + samples/Kconfig | 7 +++++++ + samples/kdbus/Makefile | 2 +- + 2 files changed, 8 insertions(+), 1 deletion(-) + +diff --git a/samples/Kconfig b/samples/Kconfig +index 224ebb46bed5..a4c6b2f8fa85 100644 +--- a/samples/Kconfig ++++ b/samples/Kconfig +@@ -55,6 +55,13 @@ config SAMPLE_KDB + Build an example of how to dynamically add the hello + command to the kdb shell. + ++config SAMPLE_KDBUS ++ bool "Build kdbus API example" ++ depends on KDBUS ++ help ++ Build an example of how the kdbus API can be used from ++ userspace. ++ + config SAMPLE_RPMSG_CLIENT + tristate "Build rpmsg client sample -- loadable modules only" + depends on RPMSG && m +diff --git a/samples/kdbus/Makefile b/samples/kdbus/Makefile +index e714602b6260..137f84272099 100644 +--- a/samples/kdbus/Makefile ++++ b/samples/kdbus/Makefile +@@ -1,7 +1,7 @@ + # kbuild trick to avoid linker error. Can be omitted if a module is built. + obj- := dummy.o + +-hostprogs-y += kdbus-workers ++hostprogs-$(CONFIG_SAMPLE_KDBUS) += kdbus-workers + + always := $(hostprogs-y) + diff --git a/samples-kdbus-drop-wrong-include.patch b/samples-kdbus-drop-wrong-include.patch new file mode 100644 index 000000000..f81cf5783 --- /dev/null +++ b/samples-kdbus-drop-wrong-include.patch @@ -0,0 +1,32 @@ +From: David Herrmann <dh.herrmann@gmail.com> +Date: Mon, 16 Mar 2015 10:17:10 +0100 +Subject: [PATCH] samples/kdbus: drop wrong include + +There is no reason to use ./include/uapi/ directly from samples. If your +system headers are not up-to-date, you _need_ to run "make +headers-install" (which will install them to ./usr/ in your kernel tree) +before building the examples. Otherwise, you will get warnings and build +failures. + +Once ./usr/ is updated with the correct headers, it contains everything we +need, so drop -Iinclude/uapi from the kdbus-workers CFLAGS. + +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + samples/kdbus/Makefile | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/samples/kdbus/Makefile b/samples/kdbus/Makefile +index eee9b9aed632..e714602b6260 100644 +--- a/samples/kdbus/Makefile ++++ b/samples/kdbus/Makefile +@@ -5,7 +5,5 @@ hostprogs-y += kdbus-workers + + always := $(hostprogs-y) + +-HOSTCFLAGS_kdbus-workers.o += \ +- -I$(objtree)/usr/include/ \ +- -I$(objtree)/include/uapi/ ++HOSTCFLAGS_kdbus-workers.o += -I$(objtree)/usr/include + HOSTLOADLIBES_kdbus-workers := -lrt diff --git a/samples-kdbus-fix-operator-precedence-issue-in-KDBUS.patch b/samples-kdbus-fix-operator-precedence-issue-in-KDBUS.patch new file mode 100644 index 000000000..b7d0fccff --- /dev/null +++ b/samples-kdbus-fix-operator-precedence-issue-in-KDBUS.patch @@ -0,0 +1,33 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Wed, 10 Jun 2015 00:00:07 +0300 +Subject: [PATCH] samples/kdbus: fix operator precedence issue in + KDBUS_ITEM_NEXT macro + +`item' argument in KDBUS_ITEM_NEXT macro is not enclosed into +parentheses when the cast operator is applied, which leads to improper +type conversion if `item' is supplied as a complex expression, e.g. + + KDBUS_ITEM_NEXT(condition ? a : b) + +Use parentheses properly to guarantee right precedence. + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + samples/kdbus/kdbus-api.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/samples/kdbus/kdbus-api.h b/samples/kdbus/kdbus-api.h +index 2de4d6a8c51e..fab873b89d97 100644 +--- a/samples/kdbus/kdbus-api.h ++++ b/samples/kdbus/kdbus-api.h +@@ -8,7 +8,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)) ++ (typeof(item))((uint8_t *)(item) + KDBUS_ALIGN8((item)->size)) + #define KDBUS_FOREACH(iter, first, _size) \ + for (iter = (first); \ + ((uint8_t *)(iter) < (uint8_t *)(first) + (_size)) && \ diff --git a/samples-kdbus-stub-out-code-for-glibc-2.7.patch b/samples-kdbus-stub-out-code-for-glibc-2.7.patch new file mode 100644 index 000000000..5525c1048 --- /dev/null +++ b/samples-kdbus-stub-out-code-for-glibc-2.7.patch @@ -0,0 +1,83 @@ +From: Daniel Mack <daniel@zonque.org> +Date: Fri, 3 Apr 2015 12:41:52 +0200 +Subject: [PATCH] samples/kdbus: stub out code for glibc < 2.7 + +Andrew Morton reports the following build error in samples/kdbus on Fedora +Core 6: + + samples/kdbus/kdbus-workers.c:73:26: error: sys/signalfd.h: No such file or directory + samples/kdbus/kdbus-workers.c: In function 'master_new': + samples/kdbus/kdbus-workers.c:231: warning: implicit declaration of function 'signalfd' + samples/kdbus/kdbus-workers.c:231: error: 'SFD_CLOEXEC' undeclared (first use in this function) + samples/kdbus/kdbus-workers.c:231: error: (Each undeclared identifier is reported only once + samples/kdbus/kdbus-workers.c:231: error: for each function it appears in.) + samples/kdbus/kdbus-workers.c: In function 'master_handle_signal': + samples/kdbus/kdbus-workers.c:406: error: storage size of 'val' isn't known + samples/kdbus/kdbus-workers.c:406: warning: unused variable 'val' + samples/kdbus/kdbus-workers.c: In function 'child_run': + samples/kdbus/kdbus-workers.c:773: error: 'CLOCK_MONOTONIC_COARSE' undeclared (first use in this function) + samples/kdbus/kdbus-workers.c: In function 'bus_open_connection': + samples/kdbus/kdbus-workers.c:1038: error: 'O_CLOEXEC' undeclared (first use in this function) + samples/kdbus/kdbus-workers.c: In function 'bus_make': + samples/kdbus/kdbus-workers.c:1275: error: 'O_CLOEXEC' undeclared (first use in this function) + +Fedora Core 6 was released in 2006, which predates the introduction of +signalfds in the kernel (v2.6.22, 2007). + +The example cannot be built without signalfds, and kbuild cannot depend on +specific features of the local libc when building userspace executables, so +we have to work around the issue by checking for specific glibc versions at +compile time and stub the entire thing if it can't be compiled. + +Reported-by: Andrew Morton <akpm@linux-foundation.org> +Signed-off-by: Daniel Mack <daniel@zonque.org> +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +--- + samples/kdbus/kdbus-workers.c | 23 +++++++++++++++++++++-- + 1 file changed, 21 insertions(+), 2 deletions(-) + +diff --git a/samples/kdbus/kdbus-workers.c b/samples/kdbus/kdbus-workers.c +index d331e0186899..c3ba958639f3 100644 +--- a/samples/kdbus/kdbus-workers.c ++++ b/samples/kdbus/kdbus-workers.c +@@ -57,6 +57,12 @@ + * top-down, but requires some forward-declarations. Just ignore those. + */ + ++#include <stdio.h> ++#include <stdlib.h> ++ ++/* glibc < 2.7 does not ship sys/signalfd.h */ ++#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 7 ++ + #include <ctype.h> + #include <errno.h> + #include <fcntl.h> +@@ -65,8 +71,6 @@ + #include <stdbool.h> + #include <stddef.h> + #include <stdint.h> +-#include <stdio.h> +-#include <stdlib.h> + #include <string.h> + #include <sys/mman.h> + #include <sys/poll.h> +@@ -1324,3 +1328,18 @@ static int bus_make(uid_t uid, const char *name) + + return fd; + } ++ ++#else ++ ++#warning "Skipping compilation due to unsupported libc version" ++ ++int main(int argc, char **argv) ++{ ++ fprintf(stderr, ++ "Compilation of %s was skipped due to unsupported libc.\n", ++ argv[0]); ++ ++ return EXIT_FAILURE; ++} ++ ++#endif /* libc sanity check */ diff --git a/samples-kdbus-use-parentheses-uniformly-in-KDBUS_FOR.patch b/samples-kdbus-use-parentheses-uniformly-in-KDBUS_FOR.patch new file mode 100644 index 000000000..6860fe635 --- /dev/null +++ b/samples-kdbus-use-parentheses-uniformly-in-KDBUS_FOR.patch @@ -0,0 +1,32 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Wed, 10 Jun 2015 00:00:08 +0300 +Subject: [PATCH] samples/kdbus: use parentheses uniformly in KDBUS_FOREACH + macro + +Enclose all arguments into parentheses to stay consistent across the +whole macro. + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + samples/kdbus/kdbus-api.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/samples/kdbus/kdbus-api.h b/samples/kdbus/kdbus-api.h +index fab873b89d97..7f3abae18396 100644 +--- a/samples/kdbus/kdbus-api.h ++++ b/samples/kdbus/kdbus-api.h +@@ -10,10 +10,10 @@ + #define KDBUS_ITEM_NEXT(item) \ + (typeof(item))((uint8_t *)(item) + KDBUS_ALIGN8((item)->size)) + #define KDBUS_FOREACH(iter, first, _size) \ +- for (iter = (first); \ ++ for ((iter) = (first); \ + ((uint8_t *)(iter) < (uint8_t *)(first) + (_size)) && \ + ((uint8_t *)(iter) >= (uint8_t *)(first)); \ +- iter = (void *)(((uint8_t *)iter) + KDBUS_ALIGN8((iter)->size))) ++ (iter) = (void *)((uint8_t *)(iter) + KDBUS_ALIGN8((iter)->size))) + + static inline int kdbus_cmd_bus_make(int control_fd, struct kdbus_cmd *cmd) + { diff --git a/selftest-kdbus-enable-cross-compilation.patch b/selftest-kdbus-enable-cross-compilation.patch new file mode 100644 index 000000000..77f35cae0 --- /dev/null +++ b/selftest-kdbus-enable-cross-compilation.patch @@ -0,0 +1,33 @@ +From: Tyler Baker <tyler.baker@linaro.org> +Date: Wed, 1 Apr 2015 16:20:16 -0700 +Subject: [PATCH] selftest/kdbus: enable cross compilation + +Use the CC variable instead of hard coding gcc and include lib.mk. + +Signed-off-by: Tyler Baker <tyler.baker@linaro.org> +Acked-by: Shuah Khan <shuahkh@osg.samsung.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + tools/testing/selftests/kdbus/Makefile | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/tools/testing/selftests/kdbus/Makefile b/tools/testing/selftests/kdbus/Makefile +index f6cfab26f315..de8242f9b00e 100644 +--- a/tools/testing/selftests/kdbus/Makefile ++++ b/tools/testing/selftests/kdbus/Makefile +@@ -33,11 +33,13 @@ OBJS= \ + + all: kdbus-test + ++include ../lib.mk ++ + %.o: %.c +- gcc $(CFLAGS) -c $< -o $@ ++ $(CC) $(CFLAGS) -c $< -o $@ + + kdbus-test: $(OBJS) +- gcc $(CFLAGS) $^ $(LDLIBS) -o $@ ++ $(CC) $(CFLAGS) $^ $(LDLIBS) -o $@ + + run_tests: + ./kdbus-test --tap diff --git a/selftests-kdbus-drop-duplicated-code-from-__kdbus_ms.patch b/selftests-kdbus-drop-duplicated-code-from-__kdbus_ms.patch new file mode 100644 index 000000000..d5ccba2f6 --- /dev/null +++ b/selftests-kdbus-drop-duplicated-code-from-__kdbus_ms.patch @@ -0,0 +1,29 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Wed, 17 Jun 2015 19:33:27 +0300 +Subject: [PATCH] selftests/kdbus: drop duplicated code from __kdbus_msg_send() + +Set value of `size' in one step instead of four. + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + tools/testing/selftests/kdbus/kdbus-util.c | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git a/tools/testing/selftests/kdbus/kdbus-util.c b/tools/testing/selftests/kdbus/kdbus-util.c +index 5b924531d938..d35ec89cb816 100644 +--- a/tools/testing/selftests/kdbus/kdbus-util.c ++++ b/tools/testing/selftests/kdbus/kdbus-util.c +@@ -462,10 +462,7 @@ static int __kdbus_msg_send(const struct kdbus_conn *conn, + int memfd = -1; + int ret; + +- size = sizeof(*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)); ++ size = sizeof(*msg) + 3 * KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec)); + + if (dst_id == KDBUS_DST_ID_BROADCAST) + size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_filter)) + 64; diff --git a/selftests-kdbus-drop-useless-assignment.patch b/selftests-kdbus-drop-useless-assignment.patch new file mode 100644 index 000000000..2e0b65d53 --- /dev/null +++ b/selftests-kdbus-drop-useless-assignment.patch @@ -0,0 +1,33 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Wed, 17 Jun 2015 19:33:25 +0300 +Subject: [PATCH] selftests/kdbus: drop useless assignment + +Assign returned file descriptor directly to `fd', without intermediate +`ret' variable. + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + tools/testing/selftests/kdbus/kdbus-util.c | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +diff --git a/tools/testing/selftests/kdbus/kdbus-util.c b/tools/testing/selftests/kdbus/kdbus-util.c +index 6909fb9b1ce5..5b924531d938 100644 +--- a/tools/testing/selftests/kdbus/kdbus-util.c ++++ b/tools/testing/selftests/kdbus/kdbus-util.c +@@ -408,11 +408,9 @@ 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; ++ fd = syscall(__NR_memfd_create, name, MFD_ALLOW_SEALING); ++ if (fd < 0) ++ return fd; + + ret = ftruncate(fd, size); + if (ret < 0) { diff --git a/selftests-kdbus-fix-error-paths-in-__kdbus_msg_send.patch b/selftests-kdbus-fix-error-paths-in-__kdbus_msg_send.patch new file mode 100644 index 000000000..ba986ab76 --- /dev/null +++ b/selftests-kdbus-fix-error-paths-in-__kdbus_msg_send.patch @@ -0,0 +1,110 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Wed, 17 Jun 2015 19:33:28 +0300 +Subject: [PATCH] selftests/kdbus: fix error paths in __kdbus_msg_send() + +Handle errors properly, free allocated resources. + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + tools/testing/selftests/kdbus/kdbus-util.c | 31 ++++++++++++++++++------------ + 1 file changed, 19 insertions(+), 12 deletions(-) + +diff --git a/tools/testing/selftests/kdbus/kdbus-util.c b/tools/testing/selftests/kdbus/kdbus-util.c +index d35ec89cb816..9fac4b31536d 100644 +--- a/tools/testing/selftests/kdbus/kdbus-util.c ++++ b/tools/testing/selftests/kdbus/kdbus-util.c +@@ -452,8 +452,8 @@ static int __kdbus_msg_send(const struct kdbus_conn *conn, + uint64_t cmd_flags, + int cancel_fd) + { +- struct kdbus_cmd_send *cmd; +- struct kdbus_msg *msg; ++ struct kdbus_cmd_send *cmd = NULL; ++ struct kdbus_msg *msg = NULL; + const char ref1[1024 * 128 + 3] = "0123456789_0"; + const char ref2[] = "0123456789_1"; + struct kdbus_item *item; +@@ -476,14 +476,14 @@ static int __kdbus_msg_send(const struct kdbus_conn *conn, + if (write(memfd, "kdbus memfd 1234567", 19) != 19) { + ret = -errno; + kdbus_printf("writing to memfd failed: %m\n"); +- return ret; ++ goto out; + } + + ret = sys_memfd_seal_set(memfd); + if (ret < 0) { + ret = -errno; + kdbus_printf("memfd sealing failed: %m\n"); +- return ret; ++ goto out; + } + + size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_memfd)); +@@ -496,7 +496,7 @@ static int __kdbus_msg_send(const struct kdbus_conn *conn, + if (!msg) { + ret = -errno; + kdbus_printf("unable to malloc()!?\n"); +- return ret; ++ goto out; + } + + if (dst_id == KDBUS_DST_ID_BROADCAST) +@@ -514,7 +514,7 @@ static int __kdbus_msg_send(const struct kdbus_conn *conn, + if (timeout) { + ret = clock_gettime(CLOCK_MONOTONIC_COARSE, &now); + if (ret < 0) +- return ret; ++ goto out; + + msg->timeout_ns = now.tv_sec * 1000000000ULL + + now.tv_nsec + timeout; +@@ -565,6 +565,12 @@ static int __kdbus_msg_send(const struct kdbus_conn *conn, + size += KDBUS_ITEM_SIZE(sizeof(cancel_fd)); + + cmd = malloc(size); ++ if (!cmd) { ++ ret = -errno; ++ kdbus_printf("unable to malloc()!?\n"); ++ goto out; ++ } ++ + cmd->size = size; + cmd->flags = cmd_flags; + cmd->msg_address = (uintptr_t)msg; +@@ -579,12 +585,9 @@ static int __kdbus_msg_send(const struct kdbus_conn *conn, + } + + ret = kdbus_cmd_send(conn->fd, cmd); +- if (memfd >= 0) +- close(memfd); +- + if (ret < 0) { + kdbus_printf("error sending message: %d (%m)\n", ret); +- return ret; ++ goto out; + } + + if (cmd_flags & KDBUS_SEND_SYNC_REPLY) { +@@ -598,13 +601,17 @@ static int __kdbus_msg_send(const struct kdbus_conn *conn, + + ret = kdbus_free(conn, cmd->reply.offset); + if (ret < 0) +- return ret; ++ goto out; + } + ++out: + free(msg); + free(cmd); + +- return 0; ++ if (memfd >= 0) ++ close(memfd); ++ ++ return ret < 0 ? ret : 0; + } + + int kdbus_msg_send(const struct kdbus_conn *conn, const char *name, diff --git a/selftests-kdbus-fix-gitignore.patch b/selftests-kdbus-fix-gitignore.patch new file mode 100644 index 000000000..aac65d56a --- /dev/null +++ b/selftests-kdbus-fix-gitignore.patch @@ -0,0 +1,24 @@ +From: David Herrmann <dh.herrmann@gmail.com> +Date: Mon, 16 Mar 2015 10:17:13 +0100 +Subject: [PATCH] selftests/kdbus: fix gitignore + +Drop unused elements from .gitignore (which are leftovers when +documentation was placed in the same directory). +Add "kdbus-test" to .gitignore, which is the test binary of all kdbus +selftests. + +Signed-off-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + tools/testing/selftests/kdbus/.gitignore | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/tools/testing/selftests/kdbus/.gitignore b/tools/testing/selftests/kdbus/.gitignore +index 7b421f76c888..d3ef42f6ada6 100644 +--- a/tools/testing/selftests/kdbus/.gitignore ++++ b/tools/testing/selftests/kdbus/.gitignore +@@ -1,3 +1 @@ +-*.7 +-manpage.* +-*.proc ++kdbus-test diff --git a/selftests-kdbus-fix-precedence-issues-in-macros.patch b/selftests-kdbus-fix-precedence-issues-in-macros.patch new file mode 100644 index 000000000..9e8984333 --- /dev/null +++ b/selftests-kdbus-fix-precedence-issues-in-macros.patch @@ -0,0 +1,53 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Wed, 10 Jun 2015 00:00:04 +0300 +Subject: [PATCH] selftests/kdbus: fix precedence issues in macros + +`item' argument in KDBUS_ITEM_NEXT macro is not enclosed into +parentheses when the cast operator is applied, which leads to improper +type conversion if `item' is supplied as a complex expression, e.g. + + KDBUS_ITEM_NEXT(condition ? a : b) + +RUN_CLONE_CHILD macro has similar issue, missing parentheses around +`clone_ret' when using indirection operator. + +Use parentheses properly to guarantee right precedence. + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + tools/testing/selftests/kdbus/kdbus-util.h | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/tools/testing/selftests/kdbus/kdbus-util.h b/tools/testing/selftests/kdbus/kdbus-util.h +index b53b03f0565c..df5721ee8f54 100644 +--- a/tools/testing/selftests/kdbus/kdbus-util.h ++++ b/tools/testing/selftests/kdbus/kdbus-util.h +@@ -27,7 +27,7 @@ + #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)) ++ (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) && \ +@@ -104,7 +104,7 @@ extern int kdbus_util_verbose; + _setup_; \ + efd = eventfd(0, EFD_CLOEXEC); \ + ASSERT_RETURN(efd >= 0); \ +- *clone_ret = 0; \ ++ *(clone_ret) = 0; \ + pid = syscall(__NR_clone, flags, NULL); \ + if (pid == 0) { \ + eventfd_t event_status = 0; \ +@@ -129,7 +129,7 @@ extern int kdbus_util_verbose; + ret = TEST_OK; \ + } else { \ + ret = -errno; \ +- *clone_ret = -errno; \ ++ *(clone_ret) = -errno; \ + } \ + close(efd); \ + ret; \ diff --git a/selftests-kdbus-fix-trivial-style-issues.patch b/selftests-kdbus-fix-trivial-style-issues.patch new file mode 100644 index 000000000..e12d1219e --- /dev/null +++ b/selftests-kdbus-fix-trivial-style-issues.patch @@ -0,0 +1,98 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Wed, 10 Jun 2015 00:00:03 +0300 +Subject: [PATCH] selftests/kdbus: fix trivial style issues + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + tools/testing/selftests/kdbus/kdbus-enum.h | 1 + + tools/testing/selftests/kdbus/kdbus-util.c | 2 +- + tools/testing/selftests/kdbus/kdbus-util.h | 21 +++++++++------------ + 3 files changed, 11 insertions(+), 13 deletions(-) + +diff --git a/tools/testing/selftests/kdbus/kdbus-enum.h b/tools/testing/selftests/kdbus/kdbus-enum.h +index a67cec3512a7..ed28cca26906 100644 +--- a/tools/testing/selftests/kdbus/kdbus-enum.h ++++ b/tools/testing/selftests/kdbus/kdbus-enum.h +@@ -6,6 +6,7 @@ + * 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); +diff --git a/tools/testing/selftests/kdbus/kdbus-util.c b/tools/testing/selftests/kdbus/kdbus-util.c +index 9fac4b31536d..29a0cb1aace2 100644 +--- a/tools/testing/selftests/kdbus/kdbus-util.c ++++ b/tools/testing/selftests/kdbus/kdbus-util.c +@@ -1355,7 +1355,7 @@ static int all_ids_are_mapped(const char *path) + return 0; + } + +-int all_uids_gids_are_mapped() ++int all_uids_gids_are_mapped(void) + { + int ret; + +diff --git a/tools/testing/selftests/kdbus/kdbus-util.h b/tools/testing/selftests/kdbus/kdbus-util.h +index 50ff07140bdd..b53b03f0565c 100644 +--- a/tools/testing/selftests/kdbus/kdbus-util.h ++++ b/tools/testing/selftests/kdbus/kdbus-util.h +@@ -7,6 +7,7 @@ + * Free Software Foundation; either version 2.1 of the License, or (at + * your option) any later version. + */ ++ + #pragma once + + #define BIT(X) (1 << (X)) +@@ -30,24 +31,22 @@ + #define KDBUS_ITEM_FOREACH(item, head, first) \ + for (item = (head)->first; \ + ((uint8_t *)(item) < (uint8_t *)(head) + (head)->size) && \ +- ((uint8_t *)(item) >= (uint8_t *)(head)); \ ++ ((uint8_t *)(item) >= (uint8_t *)(head)); \ + item = KDBUS_ITEM_NEXT(item)) + #define KDBUS_FOREACH(iter, first, _size) \ + for (iter = (first); \ + ((uint8_t *)(iter) < (uint8_t *)(first) + (_size)) && \ + ((uint8_t *)(iter) >= (uint8_t *)(first)); \ +- iter = (void*)(((uint8_t *)iter) + KDBUS_ALIGN8((iter)->size))) +- ++ iter = (void *)(((uint8_t *)iter) + KDBUS_ALIGN8((iter)->size))) + +-#define _KDBUS_ATTACH_BITS_SET_NR (__builtin_popcountll(_KDBUS_ATTACH_ALL)) ++#define _KDBUS_ATTACH_BITS_SET_NR (__builtin_popcountll(_KDBUS_ATTACH_ALL)) + + /* Sum of KDBUS_ITEM_* that reflects _KDBUS_ATTACH_ALL */ +-#define KDBUS_ATTACH_ITEMS_TYPE_SUM \ +- ((((_KDBUS_ATTACH_BITS_SET_NR - 1) * \ +- ((_KDBUS_ATTACH_BITS_SET_NR - 1) + 1)) / 2 ) + \ ++#define KDBUS_ATTACH_ITEMS_TYPE_SUM \ ++ ((((_KDBUS_ATTACH_BITS_SET_NR - 1) * \ ++ ((_KDBUS_ATTACH_BITS_SET_NR - 1) + 1)) / 2) + \ + (_KDBUS_ITEM_ATTACH_BASE * _KDBUS_ATTACH_BITS_SET_NR)) + +- + #define POOL_SIZE (16 * 1024LU * 1024LU) + + #define UNPRIV_UID 65534 +@@ -207,14 +206,12 @@ int kdbus_add_match_id(struct kdbus_conn *conn, uint64_t cookie, + uint64_t type, uint64_t id); + int kdbus_add_match_empty(struct kdbus_conn *conn); + +-int all_uids_gids_are_mapped(); ++int all_uids_gids_are_mapped(void); + int drop_privileges(uid_t uid, gid_t gid); + uint64_t now(clockid_t clock); + char *unique_name(const char *prefix); + +-int userns_map_uid_gid(pid_t pid, +- const char *map_uid, +- const char *map_gid); ++int userns_map_uid_gid(pid_t pid, const char *map_uid, const char *map_gid); + int test_is_capable(int cap, ...); + int config_user_ns_is_enabled(void); + int config_auditsyscall_is_enabled(void); diff --git a/selftests-kdbus-handle-cap_get_proc-error-properly.patch b/selftests-kdbus-handle-cap_get_proc-error-properly.patch new file mode 100644 index 000000000..d26f61f2a --- /dev/null +++ b/selftests-kdbus-handle-cap_get_proc-error-properly.patch @@ -0,0 +1,26 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Wed, 17 Jun 2015 19:33:24 +0300 +Subject: [PATCH] selftests/kdbus: handle cap_get_proc() error properly + +Fix typo in checking error value of cap_get_proc(): cap -> caps + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + tools/testing/selftests/kdbus/kdbus-util.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/testing/selftests/kdbus/kdbus-util.c b/tools/testing/selftests/kdbus/kdbus-util.c +index 4b376ecfdbed..6909fb9b1ce5 100644 +--- a/tools/testing/selftests/kdbus/kdbus-util.c ++++ b/tools/testing/selftests/kdbus/kdbus-util.c +@@ -1547,7 +1547,7 @@ int test_is_capable(int cap, ...) + cap_t caps; + + caps = cap_get_proc(); +- if (!cap) { ++ if (!caps) { + ret = -errno; + kdbus_printf("error cap_get_proc(): %d (%m)\n", ret); + return ret; diff --git a/selftests-kdbus-install-kdbus-test.patch b/selftests-kdbus-install-kdbus-test.patch new file mode 100644 index 000000000..abaef234d --- /dev/null +++ b/selftests-kdbus-install-kdbus-test.patch @@ -0,0 +1,27 @@ +From: Tyler Baker <tyler.baker@linaro.org> +Date: Tue, 21 Apr 2015 15:50:51 -0700 +Subject: [PATCH] selftests/kdbus: install kdbus-test + +Set TEST_PROGS so that kdbus-test is installed. + +Acked-by: Michael Ellerman <mpe@ellerman.id.au> +Signed-off-by: Tyler Baker <tyler.baker@linaro.org> +Cc: Shuah Khan <shuahkh@osg.samsung.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + tools/testing/selftests/kdbus/Makefile | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/tools/testing/selftests/kdbus/Makefile b/tools/testing/selftests/kdbus/Makefile +index 7ad587b3c767..8f36cb5667cc 100644 +--- a/tools/testing/selftests/kdbus/Makefile ++++ b/tools/testing/selftests/kdbus/Makefile +@@ -40,6 +40,8 @@ include ../lib.mk + kdbus-test: $(OBJS) + $(CC) $(CFLAGS) $^ $(LDLIBS) -o $@ + ++TEST_PROGS := kdbus-test ++ + run_tests: + ./kdbus-test --tap + diff --git a/selftests-kdbus-remove-useless-initializations-from-.patch b/selftests-kdbus-remove-useless-initializations-from-.patch new file mode 100644 index 000000000..c8049c0e2 --- /dev/null +++ b/selftests-kdbus-remove-useless-initializations-from-.patch @@ -0,0 +1,40 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Wed, 17 Jun 2015 19:33:26 +0300 +Subject: [PATCH] selftests/kdbus: remove useless initializations from + kdbus_clone_userns_test() + +Do not initialize efd, unpriv_conn_id, userns_conn_id and monitor. These +vars are assigned to values later in code while initial values are never +used. + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + tools/testing/selftests/kdbus/test-metadata-ns.c | 9 +++------ + 1 file changed, 3 insertions(+), 6 deletions(-) + +diff --git a/tools/testing/selftests/kdbus/test-metadata-ns.c b/tools/testing/selftests/kdbus/test-metadata-ns.c +index 2cb1d4d2a5be..ccdfae06922b 100644 +--- a/tools/testing/selftests/kdbus/test-metadata-ns.c ++++ b/tools/testing/selftests/kdbus/test-metadata-ns.c +@@ -281,16 +281,13 @@ out: + static int kdbus_clone_userns_test(const char *bus, + struct kdbus_conn *conn) + { +- int ret; +- int status; +- int efd = -1; ++ int ret, status, efd; + pid_t pid, ppid; +- uint64_t unpriv_conn_id = 0; +- uint64_t userns_conn_id = 0; ++ uint64_t unpriv_conn_id, userns_conn_id; + struct kdbus_msg *msg; + const struct kdbus_item *item; + struct kdbus_pids expected_pids; +- struct kdbus_conn *monitor = NULL; ++ struct kdbus_conn *monitor; + + kdbus_printf("STARTING TEST 'metadata-ns'.\n"); + diff --git a/selftests-kdbus-use-parentheses-in-iteration-macros-.patch b/selftests-kdbus-use-parentheses-in-iteration-macros-.patch new file mode 100644 index 000000000..b20c464e7 --- /dev/null +++ b/selftests-kdbus-use-parentheses-in-iteration-macros-.patch @@ -0,0 +1,39 @@ +From: Sergei Zviagintsev <sergei@s15v.net> +Date: Wed, 10 Jun 2015 00:00:05 +0300 +Subject: [PATCH] selftests/kdbus: use parentheses in iteration macros + uniformly + +Enclose all arguments into parentheses in KDBUS_ITEM_FOREACH and +KDBUS_FOREACH macros to stay consistent across the whole macro. + +Signed-off-by: Sergei Zviagintsev <sergei@s15v.net> +Reviewed-by: David Herrmann <dh.herrmann@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + tools/testing/selftests/kdbus/kdbus-util.h | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/tools/testing/selftests/kdbus/kdbus-util.h b/tools/testing/selftests/kdbus/kdbus-util.h +index df5721ee8f54..d1a0f1b4d0eb 100644 +--- a/tools/testing/selftests/kdbus/kdbus-util.h ++++ b/tools/testing/selftests/kdbus/kdbus-util.h +@@ -29,15 +29,15 @@ + #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; \ ++ for ((item) = (head)->first; \ + ((uint8_t *)(item) < (uint8_t *)(head) + (head)->size) && \ + ((uint8_t *)(item) >= (uint8_t *)(head)); \ +- item = KDBUS_ITEM_NEXT(item)) ++ (item) = KDBUS_ITEM_NEXT(item)) + #define KDBUS_FOREACH(iter, first, _size) \ +- for (iter = (first); \ ++ for ((iter) = (first); \ + ((uint8_t *)(iter) < (uint8_t *)(first) + (_size)) && \ + ((uint8_t *)(iter) >= (uint8_t *)(first)); \ +- iter = (void *)(((uint8_t *)iter) + KDBUS_ALIGN8((iter)->size))) ++ (iter) = (void *)((uint8_t *)(iter) + KDBUS_ALIGN8((iter)->size))) + + #define _KDBUS_ATTACH_BITS_SET_NR (__builtin_popcountll(_KDBUS_ATTACH_ALL)) + @@ -1,4 +1,4 @@ fe9dc0f6729f36400ea81aa41d614c37 linux-4.1.tar.xz 84e34c2f58901edcc5c840fe9893c02e perf-man-4.1.tar.gz bfd9e8391b982eedf8037d697ab7c1b6 patch-4.2-rc1.xz -db7c9450437995d8bb6f2fcc577ac612 patch-4.2-rc1-git1.xz +8909d96e27719907f8126216d6a8b4f5 patch-4.2-rc1-git2.xz |