From 57f0f512b273f60d52568b8c6b77e17f5636edc0 Mon Sep 17 00:00:00 2001 From: André Fabian Silva Delgado Date: Wed, 5 Aug 2015 17:04:01 -0300 Subject: Initial import --- tools/testing/selftests/kdbus/test-fd.c | 789 ++++++++++++++++++++++++++++++++ 1 file changed, 789 insertions(+) create mode 100644 tools/testing/selftests/kdbus/test-fd.c (limited to 'tools/testing/selftests/kdbus/test-fd.c') diff --git a/tools/testing/selftests/kdbus/test-fd.c b/tools/testing/selftests/kdbus/test-fd.c new file mode 100644 index 000000000..2ae0f5ae8 --- /dev/null +++ b/tools/testing/selftests/kdbus/test-fd.c @@ -0,0 +1,789 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} -- cgit v1.2.3-54-g00ecf