168 lines
5.3 KiB
Diff
168 lines
5.3 KiB
Diff
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Mon, 20 Apr 2015 16:20:59 +0200
|
|
Subject: [PATCH] kdbus: copy small ioctl payloads to stack
|
|
|
|
Right now, we use memdup_user() on all ioctl payloads. However, most of
|
|
the time an ioctl payload is pretty small. 512 bytes on stack seem
|
|
reasonable (similar to what poll() does) to speed up small ioctl payloads.
|
|
Add a command-buffer to kdbus_args and use it instead of kmalloc() for
|
|
reasonably small payloads.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Acked-by: Daniel Mack <daniel@zonque.org>
|
|
---
|
|
ipc/kdbus/handle.c | 30 ++++++++++++++++++++++++++----
|
|
ipc/kdbus/handle.h | 5 +++++
|
|
ipc/kdbus/util.c | 45 ---------------------------------------------
|
|
ipc/kdbus/util.h | 1 -
|
|
4 files changed, 31 insertions(+), 50 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/handle.c b/ipc/kdbus/handle.c
|
|
index 6230c7ef4347..07527990a051 100644
|
|
--- a/ipc/kdbus/handle.c
|
|
+++ b/ipc/kdbus/handle.c
|
|
@@ -146,11 +146,32 @@ static int kdbus_args_negotiate(struct kdbus_args *args)
|
|
int __kdbus_args_parse(struct kdbus_args *args, void __user *argp,
|
|
size_t type_size, size_t items_offset, void **out)
|
|
{
|
|
+ u64 user_size;
|
|
int ret, i;
|
|
|
|
- args->cmd = kdbus_memdup_user(argp, type_size, KDBUS_CMD_MAX_SIZE);
|
|
- if (IS_ERR(args->cmd))
|
|
- return PTR_ERR(args->cmd);
|
|
+ ret = kdbus_copy_from_user(&user_size, argp, sizeof(user_size));
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ if (user_size < type_size)
|
|
+ return -EINVAL;
|
|
+ if (user_size > KDBUS_CMD_MAX_SIZE)
|
|
+ return -EMSGSIZE;
|
|
+
|
|
+ if (user_size <= sizeof(args->cmd_buf)) {
|
|
+ if (copy_from_user(args->cmd_buf, argp, user_size))
|
|
+ return -EFAULT;
|
|
+ args->cmd = (void*)args->cmd_buf;
|
|
+ } else {
|
|
+ args->cmd = memdup_user(argp, user_size);
|
|
+ if (IS_ERR(args->cmd))
|
|
+ return PTR_ERR(args->cmd);
|
|
+ }
|
|
+
|
|
+ if (args->cmd->size != user_size) {
|
|
+ ret = -EINVAL;
|
|
+ goto error;
|
|
+ }
|
|
|
|
args->cmd->return_flags = 0;
|
|
args->user = argp;
|
|
@@ -207,7 +228,8 @@ int kdbus_args_clear(struct kdbus_args *args, int ret)
|
|
if (put_user(args->cmd->return_flags,
|
|
&args->user->return_flags))
|
|
ret = -EFAULT;
|
|
- kfree(args->cmd);
|
|
+ if (args->cmd != (void*)args->cmd_buf)
|
|
+ kfree(args->cmd);
|
|
args->cmd = NULL;
|
|
}
|
|
|
|
diff --git a/ipc/kdbus/handle.h b/ipc/kdbus/handle.h
|
|
index 93a372d554a2..13c59d975728 100644
|
|
--- a/ipc/kdbus/handle.h
|
|
+++ b/ipc/kdbus/handle.h
|
|
@@ -45,6 +45,7 @@ struct kdbus_arg {
|
|
* @argv: array of items this command supports
|
|
* @user: set by parser to user-space location of current command
|
|
* @cmd: set by parser to kernel copy of command payload
|
|
+ * @cmd_buf: 512 bytes inline buf to avoid kmalloc() on small cmds
|
|
* @items: points to item array in @cmd
|
|
* @items_size: size of @items in bytes
|
|
*
|
|
@@ -52,6 +53,9 @@ struct kdbus_arg {
|
|
* The ioctl handler has to pre-fill the flags and allowed items before passing
|
|
* the object to kdbus_args_parse(). The parser will copy the command payload
|
|
* into kernel-space and verify the correctness of the data.
|
|
+ *
|
|
+ * We use a 512 bytes buffer for small command payloads, to be allocated on
|
|
+ * stack on syscall entrance.
|
|
*/
|
|
struct kdbus_args {
|
|
u64 allowed_flags;
|
|
@@ -60,6 +64,7 @@ struct kdbus_args {
|
|
|
|
struct kdbus_cmd __user *user;
|
|
struct kdbus_cmd *cmd;
|
|
+ u8 cmd_buf[512];
|
|
|
|
struct kdbus_item *items;
|
|
size_t items_size;
|
|
diff --git a/ipc/kdbus/util.c b/ipc/kdbus/util.c
|
|
index eaa806a27997..72b188330896 100644
|
|
--- a/ipc/kdbus/util.c
|
|
+++ b/ipc/kdbus/util.c
|
|
@@ -50,51 +50,6 @@ int kdbus_copy_from_user(void *dest, void __user *user_ptr, size_t size)
|
|
}
|
|
|
|
/**
|
|
- * kdbus_memdup_user() - copy dynamically sized object from user-space
|
|
- * @user_ptr: user-provided source buffer
|
|
- * @sz_min: minimum object size
|
|
- * @sz_max: maximum object size
|
|
- *
|
|
- * This copies a dynamically sized object from user-space into kernel-space. We
|
|
- * require the object to have a 64bit size field at offset 0. We read it out
|
|
- * first, allocate a suitably sized buffer and then copy all data.
|
|
- *
|
|
- * The @sz_min and @sz_max parameters define possible min and max object sizes
|
|
- * so user-space cannot trigger un-bound kernel-space allocations.
|
|
- *
|
|
- * The same alignment-restrictions as described in kdbus_copy_from_user() apply.
|
|
- *
|
|
- * Return: pointer to dynamically allocated copy, or ERR_PTR() on failure.
|
|
- */
|
|
-void *kdbus_memdup_user(void __user *user_ptr, size_t sz_min, size_t sz_max)
|
|
-{
|
|
- void *ptr;
|
|
- u64 size;
|
|
- int ret;
|
|
-
|
|
- ret = kdbus_copy_from_user(&size, user_ptr, sizeof(size));
|
|
- if (ret < 0)
|
|
- return ERR_PTR(ret);
|
|
-
|
|
- if (size < sz_min)
|
|
- return ERR_PTR(-EINVAL);
|
|
-
|
|
- if (size > sz_max)
|
|
- return ERR_PTR(-EMSGSIZE);
|
|
-
|
|
- ptr = memdup_user(user_ptr, size);
|
|
- if (IS_ERR(ptr))
|
|
- return ptr;
|
|
-
|
|
- if (*(u64 *)ptr != size) {
|
|
- kfree(ptr);
|
|
- return ERR_PTR(-EINVAL);
|
|
- }
|
|
-
|
|
- return ptr;
|
|
-}
|
|
-
|
|
-/**
|
|
* kdbus_verify_uid_prefix() - verify UID prefix of a user-supplied name
|
|
* @name: user-supplied name to verify
|
|
* @user_ns: user-namespace to act in
|
|
diff --git a/ipc/kdbus/util.h b/ipc/kdbus/util.h
|
|
index 740b19880985..9fedf8ab41cd 100644
|
|
--- a/ipc/kdbus/util.h
|
|
+++ b/ipc/kdbus/util.h
|
|
@@ -64,7 +64,6 @@ int kdbus_verify_uid_prefix(const char *name, struct user_namespace *user_ns,
|
|
int kdbus_sanitize_attach_flags(u64 flags, u64 *attach_flags);
|
|
|
|
int kdbus_copy_from_user(void *dest, void __user *user_ptr, size_t size);
|
|
-void *kdbus_memdup_user(void __user *user_ptr, size_t sz_min, size_t sz_max);
|
|
|
|
struct kvec;
|
|
|