summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/kdbus/test-message.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/testing/selftests/kdbus/test-message.c')
-rw-r--r--tools/testing/selftests/kdbus/test-message.c734
1 files changed, 734 insertions, 0 deletions
diff --git a/tools/testing/selftests/kdbus/test-message.c b/tools/testing/selftests/kdbus/test-message.c
new file mode 100644
index 000000000..ddc1e0af8
--- /dev/null
+++ b/tools/testing/selftests/kdbus/test-message.c
@@ -0,0 +1,734 @@
+#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 get our own broadcasts */
+ ret = kdbus_msg_recv(sender, &msg, &offset);
+ ASSERT_RETURN(ret == 0);
+ ASSERT_RETURN(msg->cookie == cookie);
+
+ kdbus_msg_free(msg);
+
+ /* ... 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;
+}