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