From: Roberto Sassu <roberto.sassu@xxxxxxxxxx> Move bpfilter_send_req() to usermode_driver.c and rename it to umd_send_recv(). Make the latter independent from the message format by passing to it the pointers to the request and reply message buffers and the respective lengths, and by merely doing read/write operations. >From umd_send_recv(), remove the call to __stop_umh() and returning reply.status, which is message format-specific. Let the callers of umd_send_recv(), such as bpfilter_process_sockopt(), call shutdown_umh() and evaluate the reply. Consequently, remove __stop_umh(), since in bpfilter_process_sockopt() the CONFIG_INET condition is always true. In addition to the original bpfilter_send_req() implementation, support partial reads and writes, so that callers are not limited by the length of the message to send or receive. Currently, the pipe supports receiving 64 KB at once. Signed-off-by: Roberto Sassu <roberto.sassu@xxxxxxxxxx> --- include/linux/usermode_driver.h | 2 ++ kernel/usermode_driver.c | 47 ++++++++++++++++++++++++++++++- net/bpfilter/bpfilter_kern.c | 50 +++++++++------------------------ 3 files changed, 62 insertions(+), 37 deletions(-) diff --git a/include/linux/usermode_driver.h b/include/linux/usermode_driver.h index ad970416260..37b8d08cc3d 100644 --- a/include/linux/usermode_driver.h +++ b/include/linux/usermode_driver.h @@ -15,5 +15,7 @@ int umd_load_blob(struct umd_info *info, const void *data, size_t len); int umd_unload_blob(struct umd_info *info); int fork_usermode_driver(struct umd_info *info); void umd_cleanup_helper(struct umd_info *info); +int umd_send_recv(struct umd_info *info, void *in, size_t in_len, void *out, + size_t out_len); #endif /* __LINUX_USERMODE_DRIVER_H__ */ diff --git a/kernel/usermode_driver.c b/kernel/usermode_driver.c index 8303f4c7ca7..cdbfaad99d7 100644 --- a/kernel/usermode_driver.c +++ b/kernel/usermode_driver.c @@ -188,4 +188,49 @@ int fork_usermode_driver(struct umd_info *info) } EXPORT_SYMBOL_GPL(fork_usermode_driver); - +/** + * umd_send_recv - send/receive a message through the pipe + * @info: user mode driver info + * @in: request message + * @in_len: size of @in + * @out: reply message + * @out_len: size of @out + * + * Send a message to the user space process through the created pipe and read + * the reply. Partial reads and writes are supported. + * + * Return: Zero on success, -EFAULT otherwise. + */ +int umd_send_recv(struct umd_info *info, void *in, size_t in_len, void *out, + size_t out_len) +{ + loff_t pos, offset; + ssize_t n; + + if (!info->tgid) + return -EFAULT; + pos = 0; + offset = 0; + while (in_len) { + n = kernel_write(info->pipe_to_umh, in + offset, in_len, &pos); + if (n <= 0) { + pr_err("write fail %zd\n", n); + return -EFAULT; + } + in_len -= n; + offset += n; + } + pos = 0; + offset = 0; + while (out_len) { + n = kernel_read(info->pipe_from_umh, out + offset, out_len, &pos); + if (n <= 0) { + pr_err("read fail %zd\n", n); + return -EFAULT; + } + out_len -= n; + offset += n; + } + return 0; +} +EXPORT_SYMBOL_GPL(umd_send_recv); diff --git a/net/bpfilter/bpfilter_kern.c b/net/bpfilter/bpfilter_kern.c index 422ec6e7ccf..17d4df5f8fe 100644 --- a/net/bpfilter/bpfilter_kern.c +++ b/net/bpfilter/bpfilter_kern.c @@ -25,40 +25,6 @@ static void shutdown_umh(void) } } -static void __stop_umh(void) -{ - if (IS_ENABLED(CONFIG_INET)) - shutdown_umh(); -} - -static int bpfilter_send_req(struct mbox_request *req) -{ - struct mbox_reply reply; - loff_t pos = 0; - ssize_t n; - - if (!bpfilter_ops.info.tgid) - return -EFAULT; - pos = 0; - n = kernel_write(bpfilter_ops.info.pipe_to_umh, req, sizeof(*req), - &pos); - if (n != sizeof(*req)) { - pr_err("write fail %zd\n", n); - goto stop; - } - pos = 0; - n = kernel_read(bpfilter_ops.info.pipe_from_umh, &reply, sizeof(reply), - &pos); - if (n != sizeof(reply)) { - pr_err("read fail %zd\n", n); - goto stop; - } - return reply.status; -stop: - __stop_umh(); - return -EFAULT; -} - static int bpfilter_process_sockopt(struct sock *sk, int optname, sockptr_t optval, unsigned int optlen, bool is_set) @@ -70,16 +36,27 @@ static int bpfilter_process_sockopt(struct sock *sk, int optname, .addr = (uintptr_t)optval.user, .len = optlen, }; + struct mbox_reply reply; + int err; + if (sockptr_is_kernel(optval)) { pr_err("kernel access not supported\n"); return -EFAULT; } - return bpfilter_send_req(&req); + err = umd_send_recv(&bpfilter_ops.info, &req, sizeof(req), &reply, + sizeof(reply)); + if (err) { + shutdown_umh(); + return err; + } + + return reply.status; } static int start_umh(void) { struct mbox_request req = { .pid = current->pid }; + struct mbox_reply reply; int err; /* fork usermode process */ @@ -89,7 +66,8 @@ static int start_umh(void) pr_info("Loaded bpfilter_umh pid %d\n", pid_nr(bpfilter_ops.info.tgid)); /* health check that usermode process started correctly */ - if (bpfilter_send_req(&req) != 0) { + if (umd_send_recv(&bpfilter_ops.info, &req, sizeof(req), &reply, + sizeof(reply)) != 0 || reply.status != 0) { shutdown_umh(); return -EFAULT; } -- 2.25.1