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 timeout_msg_recv(struct kdbus_conn *conn) { struct kdbus_cmd_recv recv = {}; struct kdbus_msg *msg; int ret; ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &recv); if (ret < 0) { kdbus_printf("error receiving message: %d (%m)\n", ret); return -errno; } msg = (struct kdbus_msg *)(conn->buf + recv.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.offset); if (ret < 0) return ret; return 0; }
/* * 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; }
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; }
/* 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_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; }
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; }