[PATCH 3/4] io_uring/cmd: Introduce SOCKET_URING_OP_SETSOCKOPT

From: Breno Leitao
Date: Mon Jul 24 2023 - 10:24:16 EST


Add initial support for SOCKET_URING_OP_SETSOCKOPT. This new command is
similar to setsockopt. This initial implementation just cares about
SOL_SOCKET level for now. The next patch implements the generic case.

Function io_uring_cmd_setsockopt() is inspired by the function
__sys_setsockopt().

"optval" is currently copied to kernel space in io_uring_cmd_setsockopt(),
so, the setsockopt() protocol callbacks operate on kernel space memory
after io_uring handlers.

Important to say that userspace needs to keep the pointer's memory alive
until the operation is completed.

Signed-off-by: Breno Leitao <leitao@xxxxxxxxxx>
---
include/uapi/linux/io_uring.h | 1 +
io_uring/uring_cmd.c | 35 +++++++++++++++++++++++++++++++++++
2 files changed, 36 insertions(+)

diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h
index 8152151080db..3fe82df06abf 100644
--- a/include/uapi/linux/io_uring.h
+++ b/include/uapi/linux/io_uring.h
@@ -736,6 +736,7 @@ enum {
SOCKET_URING_OP_SIOCINQ = 0,
SOCKET_URING_OP_SIOCOUTQ,
SOCKET_URING_OP_GETSOCKOPT,
+ SOCKET_URING_OP_SETSOCKOPT,
};

#ifdef __cplusplus
diff --git a/io_uring/uring_cmd.c b/io_uring/uring_cmd.c
index 16c857cbf3b0..d63a3b0f93a3 100644
--- a/io_uring/uring_cmd.c
+++ b/io_uring/uring_cmd.c
@@ -207,6 +207,39 @@ static inline int io_uring_cmd_getsockopt(struct socket *sock,
return optlen;
}

+static inline int io_uring_cmd_setsockopt(struct socket *sock,
+ struct io_uring_cmd *cmd)
+{
+ void __user *optval = u64_to_user_ptr(READ_ONCE(cmd->sqe->optval));
+ int optname = READ_ONCE(cmd->sqe->optname);
+ int optlen = READ_ONCE(cmd->sqe->optlen);
+ int level = READ_ONCE(cmd->sqe->level);
+ void *koptval;
+ int err;
+
+ err = security_socket_setsockopt(sock, level, optname);
+ if (err)
+ return err;
+
+ koptval = kmalloc(optlen, GFP_KERNEL);
+ if (!koptval)
+ return -ENOMEM;
+
+ err = copy_from_user(koptval, optval, optlen);
+ if (err)
+ goto fail;
+
+ err = -EOPNOTSUPP;
+ if (level == SOL_SOCKET && !sock_use_custom_sol_socket(sock)) {
+ err = sock_setsockopt(sock, level, optname,
+ KERNEL_SOCKPTR(koptval), optlen);
+ }
+
+fail:
+ kfree(koptval);
+ return err;
+}
+
int io_uring_cmd_sock(struct io_uring_cmd *cmd, unsigned int issue_flags)
{
struct socket *sock = cmd->file->private_data;
@@ -230,6 +263,8 @@ int io_uring_cmd_sock(struct io_uring_cmd *cmd, unsigned int issue_flags)
return arg;
case SOCKET_URING_OP_GETSOCKOPT:
return io_uring_cmd_getsockopt(sock, cmd);
+ case SOCKET_URING_OP_SETSOCKOPT:
+ return io_uring_cmd_setsockopt(sock, cmd);
default:
return -EOPNOTSUPP;
}
--
2.34.1