* [PATCH RFC liburing 0/2] IORING_OP_CLONE/EXEC support and tests
@ 2024-12-09 23:44 Gabriel Krisman Bertazi
2024-12-09 23:44 ` [PATCH RFC liburing 1/2] Add IORING_OP_CLONE/EXEC support Gabriel Krisman Bertazi
2024-12-09 23:44 ` [PATCH RFC liburing 2/2] tests: Add test for CLONE/EXEC operations Gabriel Krisman Bertazi
0 siblings, 2 replies; 4+ messages in thread
From: Gabriel Krisman Bertazi @ 2024-12-09 23:44 UTC (permalink / raw)
To: axboe, asml.silence; +Cc: io-uring, josh, Gabriel Krisman Bertazi
This is the liburing counterpart of the IORING_OP_CLONE and
IORING_OP_EXEC kernel patches. Please, refer to the kernel patchset for
details.
manpages are missing. I'd like to settle down on the semantics and the
RFC stage before writing them down. Will of course make it part of a
V1.
Thanks,
Gabriel Krisman Bertazi (2):
Add IORING_OP_CLONE/EXEC support
tests: Add test for CLONE/EXEC operations
src/include/liburing.h | 25 ++
src/include/liburing/io_uring.h | 3 +
test/Makefile | 1 +
test/clone-exec.c | 436 ++++++++++++++++++++++++++++++++
4 files changed, 465 insertions(+)
create mode 100644 test/clone-exec.c
--
2.47.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH RFC liburing 1/2] Add IORING_OP_CLONE/EXEC support
2024-12-09 23:44 [PATCH RFC liburing 0/2] IORING_OP_CLONE/EXEC support and tests Gabriel Krisman Bertazi
@ 2024-12-09 23:44 ` Gabriel Krisman Bertazi
2024-12-10 21:06 ` Josh Triplett
2024-12-09 23:44 ` [PATCH RFC liburing 2/2] tests: Add test for CLONE/EXEC operations Gabriel Krisman Bertazi
1 sibling, 1 reply; 4+ messages in thread
From: Gabriel Krisman Bertazi @ 2024-12-09 23:44 UTC (permalink / raw)
To: axboe, asml.silence; +Cc: io-uring, josh, Gabriel Krisman Bertazi
Signed-off-by: Gabriel Krisman Bertazi <[email protected]>
---
src/include/liburing.h | 25 +++++++++++++++++++++++++
src/include/liburing/io_uring.h | 3 +++
2 files changed, 28 insertions(+)
diff --git a/src/include/liburing.h b/src/include/liburing.h
index 627fc47..6d344b1 100644
--- a/src/include/liburing.h
+++ b/src/include/liburing.h
@@ -1229,6 +1229,31 @@ IOURINGINLINE void io_uring_prep_socket_direct_alloc(struct io_uring_sqe *sqe,
__io_uring_set_target_fixed_file(sqe, IORING_FILE_INDEX_ALLOC - 1);
}
+static inline void io_uring_prep_clone(struct io_uring_sqe *sqe)
+{
+ io_uring_prep_rw(IORING_OP_CLONE, sqe, 0, NULL, 0, 0);
+}
+
+static inline void io_uring_prep_execveat(struct io_uring_sqe *sqe, int dfd,
+ const char *filename, char *const *argv,
+ char *const *envp, int flags)
+{
+ io_uring_prep_rw(IORING_OP_EXECVEAT, sqe, dfd, filename, 0, 0);
+ sqe->addr2 = (unsigned long)(void *)argv;
+ sqe->addr3 = (unsigned long)(void *)envp;
+ sqe->execve_flags = flags;
+}
+
+static inline void io_uring_prep_exec(struct io_uring_sqe *sqe,
+ const char *filename, char *const *argv,
+ char *const *envp)
+{
+ io_uring_prep_rw(IORING_OP_EXECVEAT, sqe, 0, filename, 0, 0);
+ sqe->addr2 = (unsigned long)(void *)argv;
+ sqe->addr3 = (unsigned long)(void *)envp;
+}
+
+
/*
* Prepare commands for sockets
*/
diff --git a/src/include/liburing/io_uring.h b/src/include/liburing/io_uring.h
index 7659198..a198969 100644
--- a/src/include/liburing/io_uring.h
+++ b/src/include/liburing/io_uring.h
@@ -73,6 +73,7 @@ struct io_uring_sqe {
__u32 futex_flags;
__u32 install_fd_flags;
__u32 nop_flags;
+ __u32 execve_flags;
};
__u64 user_data; /* data to be passed back at completion time */
/* pack this to avoid bogus arm OABI complaints */
@@ -262,6 +263,8 @@ enum io_uring_op {
IORING_OP_FTRUNCATE,
IORING_OP_BIND,
IORING_OP_LISTEN,
+ IORING_OP_CLONE,
+ IORING_OP_EXECVEAT,
/* this goes last, obviously */
IORING_OP_LAST,
--
2.47.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH RFC liburing 2/2] tests: Add test for CLONE/EXEC operations
2024-12-09 23:44 [PATCH RFC liburing 0/2] IORING_OP_CLONE/EXEC support and tests Gabriel Krisman Bertazi
2024-12-09 23:44 ` [PATCH RFC liburing 1/2] Add IORING_OP_CLONE/EXEC support Gabriel Krisman Bertazi
@ 2024-12-09 23:44 ` Gabriel Krisman Bertazi
1 sibling, 0 replies; 4+ messages in thread
From: Gabriel Krisman Bertazi @ 2024-12-09 23:44 UTC (permalink / raw)
To: axboe, asml.silence; +Cc: io-uring, josh, Gabriel Krisman Bertazi
Signed-off-by: Gabriel Krisman Bertazi <[email protected]>
---
test/Makefile | 1 +
test/clone-exec.c | 436 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 437 insertions(+)
create mode 100644 test/clone-exec.c
diff --git a/test/Makefile b/test/Makefile
index 3fc4a42..70ebd5f 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -61,6 +61,7 @@ test_srcs := \
buf-ring-nommap.c \
buf-ring-put.c \
ce593a6c480a.c \
+ clone-exec.c \
close-opath.c \
connect.c \
connect-rep.c \
diff --git a/test/clone-exec.c b/test/clone-exec.c
new file mode 100644
index 0000000..7e00345
--- /dev/null
+++ b/test/clone-exec.c
@@ -0,0 +1,436 @@
+#include <assert.h>
+#include <err.h>
+#include <inttypes.h>
+#include <sched.h>
+#include <spawn.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+ #include <sys/stat.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+char **t_envp;
+
+#define MAX_PATH 1024
+
+int test_fail_sequence(void)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ int ret;
+
+ ret = t_create_ring(10, &ring, IORING_SETUP_SUBMIT_ALL);
+ if (ret < 0) {
+ fprintf(stderr, "queue_init: %s\n", strerror(-ret));
+ return T_SETUP_SKIP;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_clone(sqe);
+ sqe->flags |= IOSQE_IO_LINK;
+
+ /* Add a command that will fail. */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_nop(sqe);
+ sqe->nop_flags = IORING_NOP_INJECT_RESULT;
+
+ /*
+ * A random magic number to be retrieved in cqe->res. Not a
+ * valid errno returned by io_uring.
+ */
+ sqe->len = -255;
+ sqe->flags |= IOSQE_IO_LINK;
+
+ /* And a NOP that will succeed */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_nop(sqe);
+
+ io_uring_submit(&ring);
+
+ if (io_uring_wait_cqes(&ring, &cqe, 3, NULL, NULL)) {
+ fprintf(stderr, "%s: Failed to wait for cqes\n", __func__);
+ return T_EXIT_FAIL;
+ }
+
+ /* Check the CLONE */
+ if (cqe->res) {
+ fprintf(stderr, "%s: failed to clone. Got %d\n",
+ __func__, cqe->res);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ io_uring_peek_cqe(&ring, &cqe);
+ if (cqe->res != -255) {
+ fprintf(stderr, "%s: This nop should have failed with 255. Got %d\n",
+ __func__, cqe->res);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ io_uring_peek_cqe(&ring, &cqe);
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "%s: This should have been -ECANCELED. Got %d\n",
+ __func__, cqe->res);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ io_uring_queue_exit(&ring);
+
+ return 0;
+}
+
+int test_bad_exec(void)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ char *command = "/usr/bin/echo";
+ char *const t_argv[] = { "/usr/bin/echo", "Hello World", NULL };
+ int ret;
+
+ ret = t_create_ring(10, &ring, IORING_SETUP_SUBMIT_ALL);
+ if (ret < 0) {
+ fprintf(stderr, "queue_init: %s\n", strerror(-ret));
+ return T_SETUP_SKIP;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_execveat(sqe, AT_FDCWD, command, t_argv, t_envp, 0);
+ io_uring_sqe_set_flags(sqe, IOSQE_IO_LINK|IOSQE_IO_HARDLINK);
+
+ io_uring_submit(&ring);
+
+ if (io_uring_wait_cqe(&ring, &cqe)) {
+ fprintf(stderr, "%s: Failed to wait for cqe\n", __func__);
+ return T_EXIT_FAIL;
+ }
+
+ /* Check the EXEC */
+ if (cqe->res != -EINVAL) {
+ fprintf(stderr, "%s: EXEC should have failed. Got %d\n",
+ __func__, cqe->res);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ io_uring_queue_exit(&ring);
+
+ return 0;
+}
+
+int test_simple_sequence(void)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ int ret, head, i, reaped = 0;
+
+ ret = t_create_ring(10, &ring, IORING_SETUP_SUBMIT_ALL);
+ if (ret < 0) {
+ fprintf(stderr, "queue_init: %s\n", strerror(-ret));
+ return T_SETUP_SKIP;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_clone(sqe);
+ sqe->flags |= IOSQE_IO_LINK;
+
+ /* And a few that will succeed */
+ for (i = 0; i < 5; i++) {
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_nop(sqe);
+ sqe->flags |= IOSQE_IO_LINK;
+ }
+
+ io_uring_submit(&ring);
+
+ if (io_uring_wait_cqes(&ring, &cqe, i+1, NULL, NULL)) {
+ fprintf(stderr, "%s: Failed to wait for cqes\n", __func__);
+ return T_EXIT_FAIL;
+ }
+
+ /* Check the CLONE */
+ if (cqe->res) {
+ fprintf(stderr, "%s: failed to clone. Got %d\n",
+ __func__, cqe->res);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ /* Check the NOPS */
+ io_uring_for_each_cqe(&ring, head, cqe) {
+ reaped++;
+ if (cqe->res) {
+ fprintf(stderr, "%s: This NOP should have succeeded. Got %d\n",
+ __func__, cqe->res);
+ return T_EXIT_FAIL;
+ }
+ } io_uring_cq_advance(&ring, reaped);
+
+ io_uring_queue_exit(&ring);
+
+ return 0;
+}
+
+
+int test_unlinked_clone_sequence(void)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ int ret;
+
+ ret = t_create_ring(10, &ring, IORING_SETUP_SUBMIT_ALL);
+ if (ret < 0) {
+ fprintf(stderr, "queue_init: %s\n", strerror(-ret));
+ return T_SETUP_SKIP;
+ }
+
+ /* Issue unlinked clone. */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_clone(sqe);
+
+ io_uring_submit(&ring);
+
+ if (io_uring_wait_cqe(&ring, &cqe)) {
+ fprintf(stderr, "%s: Failed to wait for cqes\n", __func__);
+ return T_EXIT_FAIL;
+ }
+
+ if (cqe->res != -EINVAL) {
+ fprintf(stderr,
+ "%s: Unlinked clone should have failed. Got %d\n",
+ __func__, cqe->res);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+int test_bad_link_sequence(void)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ int ret;
+
+ ret = t_create_ring(10, &ring, IORING_SETUP_SUBMIT_ALL);
+ if (ret < 0) {
+ fprintf(stderr, "queue_init: %s\n", strerror(-ret));
+ return T_SETUP_SKIP;
+ }
+
+ /* Issue link that doesn't start with clone. */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_nop(sqe);
+ sqe->flags |= IOSQE_IO_LINK;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_clone(sqe);
+ sqe->flags |= IOSQE_IO_LINK;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_nop(sqe);
+
+ io_uring_submit(&ring);
+
+ if (io_uring_wait_cqes(&ring, &cqe, 3, NULL, NULL)) {
+ fprintf(stderr, "%s: Failed to wait for cqes\n", __func__);
+ return T_EXIT_FAIL;
+ }
+
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr,
+ "%s: first NOP should have been canceled. Got %d\n",
+ __func__, cqe->res);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ io_uring_peek_cqe(&ring, &cqe);
+ if (cqe->res != -EINVAL) {
+ fprintf(stderr,
+ "%s: CLONE not starting link should've failed. "
+ "Got %d\n", __func__, cqe->res);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+#define TEST_FILE "./clone-exec-test-file"
+
+/* Execute 'touch TEST_FILE' by doing a lookup in $PATH and verify the
+ command was executed by checking the file exists. It would have been
+ better to just redirect the output and use an echo, but we have no
+ dup(2) in io_uring to redirect stdout inside the spawned task yet.
+ */
+int test_spawn_sequence(void)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ unsigned int head;
+ int ret, i, reaped = 0;
+ char *buf;
+
+ bool did_exec = false;
+ struct stat statbuf;
+
+ char *const t_argv[] = { "touch", TEST_FILE, NULL };
+
+ char *path[]= { "/usr/local/bin/", "/usr/local/sbin/", "/usr/bin/",
+ "/usr/sbin/", "/sbin", "/bin" };
+
+ ret = t_create_ring(10, &ring, IORING_SETUP_SUBMIT_ALL);
+ if (ret < 0) {
+
+ fprintf(stderr, "queue_init: %s\n", strerror(-ret));
+ return T_SETUP_SKIP;
+ }
+
+ ret = fstatat(AT_FDCWD, TEST_FILE, &statbuf, 0);
+ if (!ret) {
+ ret = unlinkat(AT_FDCWD, TEST_FILE, 0);
+ if (ret) {
+ printf("Failed to unlink tmp file\n");
+ return T_SETUP_SKIP;
+ }
+ } else if (errno == ENOENT) {
+ ret = 0;
+ } else {
+ printf("failed to fstatat %d\n", errno);
+ return T_EXIT_FAIL;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_clone(sqe);
+ /* allocate from heap to simplify freeing. */
+ sqe->user_data = (__u64) strdup("clone");
+ sqe->flags |= IOSQE_IO_LINK;
+
+ for (i = 0; i < ARRAY_SIZE(path); i++ ) {
+ buf = malloc(MAX_PATH);
+ if (!buf)
+ return -ENOMEM;
+ snprintf(buf, MAX_PATH, "%s/%s", path[i], "touch");
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_execveat(sqe, AT_FDCWD, buf, t_argv, t_envp, 0);
+ sqe->user_data = (__u64) buf;
+ io_uring_sqe_set_flags(sqe, IOSQE_IO_LINK|IOSQE_IO_HARDLINK);
+ }
+
+ io_uring_submit_and_wait(&ring, i + 1);
+
+ /* Check the CLONE */
+ io_uring_peek_cqe(&ring, &cqe);
+ if (cqe->res) {
+ fprintf(stderr, "%s: failed to clone. Got %d\n",
+ __func__, cqe->res);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ /* Check the EXEC */
+ io_uring_for_each_cqe(&ring, head, cqe) {
+ reaped++;
+
+ if (!did_exec) {
+ if (cqe->res == 0) {
+ /* An execve succeeded */
+ did_exec = true;
+ } else if (!(cqe->res == -ENOENT)) {
+ printf("EXEC %s: expecting -ENOENT got %d\n",
+ (char*)cqe->user_data, cqe->res);
+ ret = T_EXIT_FAIL;
+ }
+ /* We are looking at $PATH to find a suitable
+ * binary. Any -ENOENT before succeeding the
+ * exec is expected.
+ */
+ } else {
+ /* After an exec, further requests must be canceled. */
+ if (!(cqe->res == -ECANCELED)) {
+ printf("EXEC %s: expecting -ECANCELED got %d\n",
+ (char*)cqe->user_data, cqe->res);
+ ret = T_EXIT_FAIL;
+ }
+ }
+ free((char*)cqe->user_data);
+ } io_uring_cq_advance(&ring, reaped);
+
+
+ if (!did_exec) {
+ printf("All OP_EXEC failed!\n");
+ ret = T_EXIT_FAIL;
+ }
+
+ ret = fstatat(AT_FDCWD, TEST_FILE, &statbuf, 0);
+ if (ret) {
+ /* We might need to give the spawned command a chance to run. */
+ sleep(1);
+ ret = fstatat(AT_FDCWD, TEST_FILE, &statbuf, 0);
+ if (ret) {
+ printf("Touch didn't run? File wasn't created! errno=%d\n", errno);
+ return T_EXIT_FAIL;
+ }
+ }
+
+ io_uring_queue_exit(&ring);
+ return ret;
+}
+
+int main(int argc, char *argv[], char *envp[])
+{
+ int ret = 0;
+
+ t_envp = envp;
+
+ if (test_fail_sequence()) {
+ fprintf(stderr, "test_failed_sequence failed\n");
+ ret = T_EXIT_FAIL;
+ }
+
+ if (test_unlinked_clone_sequence()) {
+ fprintf(stderr, "test_unlinked_clone_sequence\n");
+ ret = T_EXIT_FAIL;
+ }
+
+ if (test_bad_link_sequence()) {
+ fprintf(stderr, "test_bad_link_sequence failed\n");
+ ret = T_EXIT_FAIL;
+ }
+
+ if (test_simple_sequence()) {
+ fprintf(stderr, "test_simple_sequence failed\n");
+ ret = T_EXIT_FAIL;
+ }
+
+ if (test_bad_exec()) {
+ fprintf(stderr, "test_bad_exec failed\n");
+ ret = T_EXIT_FAIL;
+ }
+
+ if (test_spawn_sequence()) {
+ fprintf(stderr, "test_spawn_sequence failed\n");
+ ret = T_EXIT_FAIL;
+ }
+
+ return ret;
+}
--
2.47.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH RFC liburing 1/2] Add IORING_OP_CLONE/EXEC support
2024-12-09 23:44 ` [PATCH RFC liburing 1/2] Add IORING_OP_CLONE/EXEC support Gabriel Krisman Bertazi
@ 2024-12-10 21:06 ` Josh Triplett
0 siblings, 0 replies; 4+ messages in thread
From: Josh Triplett @ 2024-12-10 21:06 UTC (permalink / raw)
To: Gabriel Krisman Bertazi; +Cc: axboe, asml.silence, io-uring
On Mon, Dec 09, 2024 at 06:44:20PM -0500, Gabriel Krisman Bertazi wrote:
> Signed-off-by: Gabriel Krisman Bertazi <[email protected]>
One issue noted below; with that fixed:
Reviewed-by: Josh Triplett <[email protected]>
> --- a/src/include/liburing.h
> +++ b/src/include/liburing.h
> @@ -1229,6 +1229,31 @@ IOURINGINLINE void io_uring_prep_socket_direct_alloc(struct io_uring_sqe *sqe,
> __io_uring_set_target_fixed_file(sqe, IORING_FILE_INDEX_ALLOC - 1);
> }
>
> +static inline void io_uring_prep_clone(struct io_uring_sqe *sqe)
> +{
> + io_uring_prep_rw(IORING_OP_CLONE, sqe, 0, NULL, 0, 0);
> +}
> +
> +static inline void io_uring_prep_execveat(struct io_uring_sqe *sqe, int dfd,
> + const char *filename, char *const *argv,
> + char *const *envp, int flags)
> +{
> + io_uring_prep_rw(IORING_OP_EXECVEAT, sqe, dfd, filename, 0, 0);
> + sqe->addr2 = (unsigned long)(void *)argv;
> + sqe->addr3 = (unsigned long)(void *)envp;
> + sqe->execve_flags = flags;
> +}
> +
> +static inline void io_uring_prep_exec(struct io_uring_sqe *sqe,
> + const char *filename, char *const *argv,
> + char *const *envp)
> +{
> + io_uring_prep_rw(IORING_OP_EXECVEAT, sqe, 0, filename, 0, 0);
Shouldn't this helper use AT_FDCWD, rather than 0?
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2024-12-10 21:06 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-12-09 23:44 [PATCH RFC liburing 0/2] IORING_OP_CLONE/EXEC support and tests Gabriel Krisman Bertazi
2024-12-09 23:44 ` [PATCH RFC liburing 1/2] Add IORING_OP_CLONE/EXEC support Gabriel Krisman Bertazi
2024-12-10 21:06 ` Josh Triplett
2024-12-09 23:44 ` [PATCH RFC liburing 2/2] tests: Add test for CLONE/EXEC operations Gabriel Krisman Bertazi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox