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; }
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; }
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; }
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); ret = kdbus_free(conn, offset); ASSERT_RETURN(ret == 0); kdbus_conn_free(sender); kdbus_conn_free(conn); return TEST_OK; }
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 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 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; }
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); item = kdbus_get_item(msg, KDBUS_ITEM_CAPS); ASSERT_EXIT(item); /* 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);
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; }
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; }
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;
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 = ioctl(conn->fd, KDBUS_CMD_RECV, &recv); if (ret < 0) { kdbus_printf("error receiving message: %d (%m)\n", -errno); return -errno; } msg = (struct kdbus_msg *)(conn->buf + recv.reply.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.reply.offset); if (ret < 0) return ret; return 0; } int kdbus_test_message_prio(struct kdbus_test_env *env) { struct kdbus_conn *a, *b; uint64_t cookie = 0; a = kdbus_hello(env->buspath, 0, NULL, 0); b = kdbus_hello(env->buspath, 0, NULL, 0); ASSERT_RETURN(a && b); ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, 25, a->id) == 0); ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -600, a->id) == 0); ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, 10, a->id) == 0); ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -35, a->id) == 0); ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -100, a->id) == 0); ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, 20, a->id) == 0); ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -15, a->id) == 0); ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -800, a->id) == 0); ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -150, a->id) == 0); ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, 10, a->id) == 0); ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -800, a->id) == 0); ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -10, a->id) == 0); ASSERT_RETURN(msg_recv_prio(a, -200, -800) == 0); ASSERT_RETURN(msg_recv_prio(a, -100, -800) == 0); ASSERT_RETURN(msg_recv_prio(a, -400, -600) == 0); ASSERT_RETURN(msg_recv_prio(a, -400, -600) == -ENOMSG); ASSERT_RETURN(msg_recv_prio(a, 10, -150) == 0); ASSERT_RETURN(msg_recv_prio(a, 10, -100) == 0); kdbus_printf("--- get priority (all)\n"); ASSERT_RETURN(kdbus_msg_recv(a, NULL, NULL) == 0); kdbus_conn_free(a); kdbus_conn_free(b); return TEST_OK; } static int kdbus_test_notify_kernel_quota(struct kdbus_test_env *env) { int ret; unsigned int i; uint64_t offset; struct kdbus_conn *conn; struct kdbus_conn *reader; struct kdbus_msg *msg = NULL; 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, message will be lost * but it will not be accounted in dropped msgs */ ret = kdbus_msg_send(conn, NULL, 0xdeadbeef, 0, 0, 0, reader->id); ASSERT_RETURN(ret == -ENOBUFS); /* 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); ret = kdbus_msg_recv(reader, &msg, &offset); ASSERT_RETURN(ret == -EOVERFLOW); /* * We lost only 3 packet since only broadcast mesg * are accounted. The connection ID add/remove notification */ ASSERT_RETURN(offset == 3); kdbus_msg_free(msg); /* Read our queue */ for (i = 0; i < KDBUS_CONN_MAX_MSGS; i++) { ret = kdbus_msg_recv_poll(reader, 100, &msg, NULL); ASSERT_RETURN(ret == 0); kdbus_msg_free(msg); } ret = kdbus_msg_recv(reader, NULL, NULL); ASSERT_RETURN(ret == -EAGAIN); kdbus_conn_free(reader); return 0; }
})); ASSERT_RETURN(ret == 0); expected_cookie++; /* Now try to send a legitimate message from B to A */ ret = kdbus_msg_send(privileged_b, NULL, expected_cookie, 0, 0, 0, privileged_a->id); ASSERT_RETURN(ret == 0); expected_cookie++; ret = kdbus_msg_send(privileged_b, NULL, expected_cookie, 0, 0, 0, KDBUS_DST_ID_BROADCAST); ASSERT_RETURN(ret == 0); /* Privileged service A tries to read its messages now */ ret = kdbus_msg_recv_poll(privileged_a, 100, &msg, &offset); ASSERT_RETURN(ret == -EOVERFLOW); /* * We have lost 1 broadcast messages, the one from unprivileged * the privileged broadcast was queued, our quota is per user */ ASSERT_RETURN(offset == 1); /* Read our queue */ for (i = 0; i < MAX_USER_TOTAL_MSGS; i++) { ret = kdbus_msg_recv_poll(privileged_a, 100, &msg, NULL); ASSERT_RETURN(ret == 0); ASSERT_RETURN(msg->dst_id == KDBUS_DST_ID_BROADCAST);