int kdbus_test_sync_reply(struct kdbus_test_env *env) { pthread_t thread; int ret; conn_a = kdbus_hello(env->buspath, 0, NULL, 0); conn_b = kdbus_hello(env->buspath, 0, NULL, 0); ASSERT_RETURN(conn_a && conn_b); pthread_create(&thread, NULL, run_thread, NULL); ret = kdbus_msg_send(conn_b, NULL, cookie, KDBUS_MSG_FLAGS_EXPECT_REPLY | KDBUS_MSG_FLAGS_SYNC_REPLY, 5000000000ULL, 0, conn_a->id); pthread_join(thread, NULL); ASSERT_RETURN(ret == 0); kdbus_printf("-- closing bus connections\n"); kdbus_conn_free(conn_a); kdbus_conn_free(conn_b); return TEST_OK; }
int kdbus_test_monitor(struct kdbus_test_env *env) { struct kdbus_conn *monitor, *conn; unsigned int cookie = 0xdeadbeef; struct kdbus_cmd_name *cmd_name; struct kdbus_msg *msg; size_t size; char *name; int ret; monitor = kdbus_hello(env->buspath, KDBUS_HELLO_MONITOR, NULL, 0); ASSERT_RETURN(monitor); /* taking a name must fail */ name = "foo.bla.blaz"; size = sizeof(*cmd_name) + strlen(name) + 1; cmd_name = alloca(size); memset(cmd_name, 0, size); strcpy(cmd_name->name, name); cmd_name->size = size; cmd_name->flags = 0; /* check that we can acquire a name */ ret = ioctl(monitor->fd, KDBUS_CMD_NAME_ACQUIRE, cmd_name); ASSERT_RETURN(ret == -1 && errno == EOPNOTSUPP); conn = kdbus_hello(env->buspath, 0, NULL, 0); ASSERT_RETURN(conn); ret = kdbus_msg_send(env->conn, NULL, cookie, 0, 0, 0, conn->id); ASSERT_RETURN(ret == 0); /* the recipient should have got the message */ ret = kdbus_msg_recv(conn, &msg); ASSERT_RETURN(ret == 0); ASSERT_RETURN(msg->cookie == cookie); kdbus_msg_free(msg); /* and so should the monitor */ ret = kdbus_msg_recv(monitor, &msg); ASSERT_RETURN(ret == 0); ASSERT_RETURN(msg->cookie == cookie); kdbus_msg_free(msg); kdbus_conn_free(monitor); kdbus_conn_free(conn); return TEST_OK; }
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; conn_a = kdbus_hello(env->buspath, 0, NULL, 0); conn_b = kdbus_hello(env->buspath, 0, NULL, 0); ASSERT_RETURN(conn_a && conn_b); fd.fd = conn_b->fd; /* * send messages that expect a reply (within 100 msec), * but never answer it. */ for (i = 0; i < n_msgs; i++) { kdbus_printf("Sending message with cookie %u ...\n", i); kdbus_msg_send(conn_b, NULL, i, KDBUS_MSG_FLAGS_EXPECT_REPLY, (i + 1) * 100ULL * 1000000ULL, 0, conn_a->id); expected |= 1ULL << i; } for (;;) { fd.events = POLLIN | POLLPRI | POLLHUP; fd.revents = 0; ret = poll(&fd, 1, (n_msgs + 1) * 100); if (ret == 0) kdbus_printf("--- timeout\n"); if (ret <= 0) break; if (fd.revents & POLLIN) timeout_msg_recv(conn_b); if (expected == 0) break; } ASSERT_RETURN(expected == 0); kdbus_conn_free(conn_a); kdbus_conn_free(conn_b); return TEST_OK; }
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; }
/** * Create new threads for receiving from multiple senders, * The 'conn_db' will be populated by newly created connections. * Caller should free all allocated connections. * * return 0 on success, negative errno on failure. */ static int kdbus_recv_in_threads(const char *bus, const char *name, struct kdbus_conn **conn_db) { int ret; unsigned int i, tid; unsigned long dst_id; unsigned long cookie = 1; unsigned int thread_nr = MAX_CONN - 1; pthread_t thread_id[MAX_CONN - 1] = {'\0'}; dst_id = name ? KDBUS_DST_ID_NAME : conn_db[0]->id; for (tid = 0, i = 1; tid < thread_nr; tid++, i++) { ret = pthread_create(&thread_id[tid], NULL, kdbus_recv_echo, (void *)conn_db[0]); if (ret < 0) { ret = -errno; kdbus_printf("error pthread_create: %d err %d (%m)\n", ret, errno); break; } /* just free before re-using */ kdbus_conn_free(conn_db[i]); conn_db[i] = NULL; /* We need to create connections here */ conn_db[i] = kdbus_hello(bus, 0, NULL, 0); if (!conn_db[i]) { ret = -errno; break; } ret = kdbus_add_match_empty(conn_db[i]); if (ret < 0) break; ret = kdbus_msg_send(conn_db[i], name, cookie++, 0, 0, 0, dst_id); if (ret < 0) break; } for (tid = 0; tid < thread_nr; tid++) { int thread_ret = 0; if (thread_id[tid]) { pthread_join(thread_id[tid], (void *)&thread_ret); if (thread_ret < 0 && ret == 0) ret = thread_ret; } } return ret; }
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); }),
int kdbus_test_conn_update(struct kdbus_test_env *env) { const struct kdbus_item *item; struct kdbus_conn *conn; struct kdbus_msg *msg; int found = 0; int ret; /* * kdbus_hello() sets all attach flags. Receive a message by this * connection, and make sure a timestamp item (just to pick one) is * present. */ conn = kdbus_hello(env->buspath, 0, NULL, 0); ASSERT_RETURN(conn); ret = kdbus_msg_send(env->conn, NULL, 0x12345678, 0, 0, 0, conn->id); ASSERT_RETURN(ret == 0); ret = kdbus_msg_recv(conn, &msg, NULL); ASSERT_RETURN(ret == 0); KDBUS_ITEM_FOREACH(item, msg, items) if (item->type == KDBUS_ITEM_TIMESTAMP) found = 1; ASSERT_RETURN(found == 1); /* * Now, modify the attach flags and repeat the action. The item must * now be missing. */ found = 0; ret = kdbus_conn_update_attach_flags(conn, _KDBUS_ATTACH_ALL & ~KDBUS_ATTACH_TIMESTAMP); ASSERT_RETURN(ret == 0); ret = kdbus_msg_send(env->conn, NULL, 0x12345678, 0, 0, 0, conn->id); ASSERT_RETURN(ret == 0); ret = kdbus_msg_recv(conn, &msg, NULL); ASSERT_RETURN(ret == 0); KDBUS_ITEM_FOREACH(item, msg, items) if (item->type == KDBUS_ITEM_TIMESTAMP) found = 1; ASSERT_RETURN(found == 0); kdbus_conn_free(conn); return TEST_OK; }
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; }
int kdbus_test_byebye(struct kdbus_test_env *env) { struct kdbus_conn *conn; struct kdbus_cmd_recv recv = {}; int ret; /* create a 2nd connection */ conn = kdbus_hello(env->buspath, 0, NULL, 0); ASSERT_RETURN(conn != NULL); ret = kdbus_add_match_empty(conn); ASSERT_RETURN(ret == 0); ret = kdbus_add_match_empty(env->conn); ASSERT_RETURN(ret == 0); /* send over 1st connection */ ret = kdbus_msg_send(env->conn, NULL, 0, 0, 0, 0, KDBUS_DST_ID_BROADCAST); ASSERT_RETURN(ret == 0); /* say byebye on the 2nd, which must fail */ ret = ioctl(conn->fd, KDBUS_CMD_BYEBYE, 0); ASSERT_RETURN(ret == -1 && errno == EBUSY); /* receive the message */ ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &recv); ASSERT_RETURN(ret == 0); ret = kdbus_free(conn, recv.offset); ASSERT_RETURN(ret == 0); /* and try again */ ret = ioctl(conn->fd, KDBUS_CMD_BYEBYE, 0); ASSERT_RETURN(ret == 0); /* a 2nd try should result in -EALREADY */ ret = ioctl(conn->fd, KDBUS_CMD_BYEBYE, 0); ASSERT_RETURN(ret == -1 && errno == EOPNOTSUPP); kdbus_conn_free(conn); 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; }
/** * 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; }
int kdbus_test_activator(struct kdbus_test_env *env) { int ret; struct kdbus_conn *activator; struct pollfd fds[2]; bool activator_done = false; struct kdbus_policy_access access[2]; access[0].type = KDBUS_POLICY_ACCESS_USER; access[0].id = 1001; access[0].access = KDBUS_POLICY_OWN; access[1].type = KDBUS_POLICY_ACCESS_WORLD; access[1].access = KDBUS_POLICY_TALK; activator = kdbus_hello_activator(env->buspath, "foo.test.activator", access, 2); ASSERT_RETURN(activator); ret = kdbus_add_match_empty(env->conn); ASSERT_RETURN(ret == 0); ret = kdbus_name_list(env->conn, KDBUS_NAME_LIST_NAMES | KDBUS_NAME_LIST_UNIQUE | KDBUS_NAME_LIST_ACTIVATORS | KDBUS_NAME_LIST_QUEUED); ASSERT_RETURN(ret == 0); ret = kdbus_msg_send(env->conn, "foo.test.activator", 0xdeafbeef, 0, 0, 0, KDBUS_DST_ID_NAME); ASSERT_RETURN(ret == 0); fds[0].fd = activator->fd; fds[1].fd = env->conn->fd; kdbus_printf("-- entering poll loop ...\n"); for (;;) { int i, nfds = sizeof(fds) / sizeof(fds[0]); for (i = 0; i < nfds; i++) { fds[i].events = POLLIN | POLLPRI; fds[i].revents = 0; } ret = poll(fds, nfds, 3000); ASSERT_RETURN(ret >= 0); ret = kdbus_name_list(env->conn, KDBUS_NAME_LIST_NAMES); ASSERT_RETURN(ret == 0); if ((fds[0].revents & POLLIN) && !activator_done) { kdbus_printf("Starter was called back!\n"); ret = kdbus_name_acquire(env->conn, "foo.test.activator", KDBUS_NAME_REPLACE_EXISTING); ASSERT_RETURN(ret == 0); activator_done = true; } if (fds[1].revents & POLLIN) { kdbus_msg_recv(env->conn, NULL); break; } } kdbus_conn_free(activator); return TEST_OK; }
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 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; }
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; }
/* 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; }
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; }
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; }