stable.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 4.9 1/3] ion: Fix use after free during ION_IOC_ALLOC
@ 2022-01-25 14:18 Lee Jones
  2022-01-25 14:18 ` [PATCH v2 4.9 2/3] ion: Protect kref from userspace manipulation Lee Jones
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Lee Jones @ 2022-01-25 14:18 UTC (permalink / raw)
  To: lee.jones; +Cc: stable, Daniel Rosenberg, Dennis Cagle, Patrick Daly

From: Daniel Rosenberg <drosen@google.com>

If a user happens to call ION_IOC_FREE during an ION_IOC_ALLOC
on the just allocated id, and the copy_to_user fails, the cleanup
code will attempt to free an already freed handle.

This adds a wrapper for ion_alloc that adds an ion_handle_get to
avoid this.

Signed-off-by: Daniel Rosenberg <drosen@google.com>
Signed-off-by: Dennis Cagle <d-cagle@codeaurora.org>
Signed-off-by: Patrick Daly <pdaly@codeaurora.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---

NB: These are Android patches that were not sent to Mainline.

Only v4.9 is affected by these issues due to refactoring.

 drivers/staging/android/ion/ion-ioctl.c | 14 +++++++++-----
 drivers/staging/android/ion/ion.c       | 15 ++++++++++++---
 drivers/staging/android/ion/ion.h       |  4 ++++
 3 files changed, 25 insertions(+), 8 deletions(-)

diff --git a/drivers/staging/android/ion/ion-ioctl.c b/drivers/staging/android/ion/ion-ioctl.c
index e3596855a7031..f260e0e70488f 100644
--- a/drivers/staging/android/ion/ion-ioctl.c
+++ b/drivers/staging/android/ion/ion-ioctl.c
@@ -96,10 +96,10 @@ long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 	{
 		struct ion_handle *handle;
 
-		handle = ion_alloc(client, data.allocation.len,
-						data.allocation.align,
-						data.allocation.heap_id_mask,
-						data.allocation.flags);
+		handle = __ion_alloc(client, data.allocation.len,
+				     data.allocation.align,
+				     data.allocation.heap_id_mask,
+				     data.allocation.flags, true);
 		if (IS_ERR(handle))
 			return PTR_ERR(handle);
 
@@ -174,10 +174,14 @@ long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 
 	if (dir & _IOC_READ) {
 		if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) {
-			if (cleanup_handle)
+			if (cleanup_handle) {
 				ion_free(client, cleanup_handle);
+				ion_handle_put(cleanup_handle);
+			}
 			return -EFAULT;
 		}
 	}
+	if (cleanup_handle)
+		ion_handle_put(cleanup_handle);
 	return ret;
 }
diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c
index aac9b38b8c25c..4f769213be1b7 100644
--- a/drivers/staging/android/ion/ion.c
+++ b/drivers/staging/android/ion/ion.c
@@ -401,9 +401,9 @@ static int ion_handle_add(struct ion_client *client, struct ion_handle *handle)
 	return 0;
 }
 
-struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
-			     size_t align, unsigned int heap_id_mask,
-			     unsigned int flags)
+struct ion_handle *__ion_alloc(struct ion_client *client, size_t len,
+			       size_t align, unsigned int heap_id_mask,
+			       unsigned int flags, bool grab_handle)
 {
 	struct ion_handle *handle;
 	struct ion_device *dev = client->dev;
@@ -453,6 +453,8 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
 		return handle;
 
 	mutex_lock(&client->lock);
+	if (grab_handle)
+		ion_handle_get(handle);
 	ret = ion_handle_add(client, handle);
 	mutex_unlock(&client->lock);
 	if (ret) {
@@ -462,6 +464,13 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
 
 	return handle;
 }
+
+struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
+			     size_t align, unsigned int heap_id_mask,
+			     unsigned int flags)
+{
+	return __ion_alloc(client, len, align, heap_id_mask, flags, false);
+}
 EXPORT_SYMBOL(ion_alloc);
 
 void ion_free_nolock(struct ion_client *client,
diff --git a/drivers/staging/android/ion/ion.h b/drivers/staging/android/ion/ion.h
index 93dafb4586e43..cfa50dfb46edc 100644
--- a/drivers/staging/android/ion/ion.h
+++ b/drivers/staging/android/ion/ion.h
@@ -109,6 +109,10 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
 			     size_t align, unsigned int heap_id_mask,
 			     unsigned int flags);
 
+struct ion_handle *__ion_alloc(struct ion_client *client, size_t len,
+			       size_t align, unsigned int heap_id_mask,
+			       unsigned int flags, bool grab_handle);
+
 /**
  * ion_free - free a handle
  * @client:	the client
-- 
2.35.0.rc0.227.g00780c9af4-goog


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH v2 4.9 2/3] ion: Protect kref from userspace manipulation
  2022-01-25 14:18 [PATCH v2 4.9 1/3] ion: Fix use after free during ION_IOC_ALLOC Lee Jones
@ 2022-01-25 14:18 ` Lee Jones
  2022-01-25 14:18 ` [PATCH v2 4.9 3/3] ion: Do not 'put' ION handle until after its final use Lee Jones
  2022-01-27 15:38 ` [PATCH v2 4.9 1/3] ion: Fix use after free during ION_IOC_ALLOC Greg KH
  2 siblings, 0 replies; 4+ messages in thread
From: Lee Jones @ 2022-01-25 14:18 UTC (permalink / raw)
  To: lee.jones; +Cc: stable, Daniel Rosenberg, Dennis Cagle

From: Daniel Rosenberg <drosen@google.com>

This separates the kref for ion handles into two components.
Userspace requests through the ioctl will hold at most one
reference to the internally used kref. All additional requests
will increment a separate counter, and the original reference is
only put once that counter hits 0. This protects the kernel from
a poorly behaving userspace.

Signed-off-by: Daniel Rosenberg <drosen@google.com>
[d-cagle@codeaurora.org: Resolve style issues]
Signed-off-by: Dennis Cagle <d-cagle@codeaurora.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---

NB: These are Android patches that were not sent to Mainline.

Only v4.9 is affected by these issues due to refactoring.

 drivers/staging/android/ion/ion-ioctl.c | 84 ++++++++++++++++++++++---
 drivers/staging/android/ion/ion.c       |  4 +-
 drivers/staging/android/ion/ion_priv.h  |  4 ++
 3 files changed, 83 insertions(+), 9 deletions(-)

diff --git a/drivers/staging/android/ion/ion-ioctl.c b/drivers/staging/android/ion/ion-ioctl.c
index f260e0e70488f..d47e9b4171e28 100644
--- a/drivers/staging/android/ion/ion-ioctl.c
+++ b/drivers/staging/android/ion/ion-ioctl.c
@@ -30,6 +30,69 @@ union ion_ioctl_arg {
 	struct ion_heap_query query;
 };
 
+/* Must hold the client lock */
+static void user_ion_handle_get(struct ion_handle *handle)
+{
+	if (handle->user_ref_count++ == 0)
+		kref_get(&handle->ref);
+}
+
+/* Must hold the client lock */
+static struct ion_handle *user_ion_handle_get_check_overflow(
+	struct ion_handle *handle)
+{
+	if (handle->user_ref_count + 1 == 0)
+		return ERR_PTR(-EOVERFLOW);
+	user_ion_handle_get(handle);
+	return handle;
+}
+
+/* passes a kref to the user ref count.
+ * We know we're holding a kref to the object before and
+ * after this call, so no need to reverify handle.
+ */
+static struct ion_handle *pass_to_user(struct ion_handle *handle)
+{
+	struct ion_client *client = handle->client;
+	struct ion_handle *ret;
+
+	mutex_lock(&client->lock);
+	ret = user_ion_handle_get_check_overflow(handle);
+	ion_handle_put_nolock(handle);
+	mutex_unlock(&client->lock);
+	return ret;
+}
+
+/* Must hold the client lock */
+static int user_ion_handle_put_nolock(struct ion_handle *handle)
+{
+	int ret;
+
+	if (--handle->user_ref_count == 0)
+		ret = ion_handle_put_nolock(handle);
+
+	return ret;
+}
+
+static void user_ion_free_nolock(struct ion_client *client,
+				 struct ion_handle *handle)
+{
+	bool valid_handle;
+
+	WARN_ON(client != handle->client);
+
+	valid_handle = ion_handle_validate(client, handle);
+	if (!valid_handle) {
+		WARN(1, "%s: invalid handle passed to free.\n", __func__);
+		return;
+	}
+	if (handle->user_ref_count == 0) {
+		WARN(1, "%s: User does not have access!\n", __func__);
+		return;
+	}
+	user_ion_handle_put_nolock(handle);
+}
+
 static int validate_ioctl_arg(unsigned int cmd, union ion_ioctl_arg *arg)
 {
 	int ret = 0;
@@ -102,7 +165,7 @@ long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 				     data.allocation.flags, true);
 		if (IS_ERR(handle))
 			return PTR_ERR(handle);
-
+		pass_to_user(handle);
 		data.allocation.handle = handle->id;
 
 		cleanup_handle = handle;
@@ -118,7 +181,7 @@ long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 			mutex_unlock(&client->lock);
 			return PTR_ERR(handle);
 		}
-		ion_free_nolock(client, handle);
+		user_ion_free_nolock(client, handle);
 		ion_handle_put_nolock(handle);
 		mutex_unlock(&client->lock);
 		break;
@@ -146,10 +209,15 @@ long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 		struct ion_handle *handle;
 
 		handle = ion_import_dma_buf_fd(client, data.fd.fd);
-		if (IS_ERR(handle))
+		if (IS_ERR(handle)) {
 			ret = PTR_ERR(handle);
-		else
-			data.handle.handle = handle->id;
+		} else {
+			handle = pass_to_user(handle);
+			if (IS_ERR(handle))
+				ret = PTR_ERR(handle);
+			else
+				data.handle.handle = handle->id;
+		}
 		break;
 	}
 	case ION_IOC_SYNC:
@@ -175,8 +243,10 @@ long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 	if (dir & _IOC_READ) {
 		if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) {
 			if (cleanup_handle) {
-				ion_free(client, cleanup_handle);
-				ion_handle_put(cleanup_handle);
+				mutex_lock(&client->lock);
+				user_ion_free_nolock(client, cleanup_handle);
+				ion_handle_put_nolock(cleanup_handle);
+				mutex_unlock(&client->lock);
 			}
 			return -EFAULT;
 		}
diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c
index 4f769213be1b7..b272f2ab87e8f 100644
--- a/drivers/staging/android/ion/ion.c
+++ b/drivers/staging/android/ion/ion.c
@@ -363,8 +363,8 @@ struct ion_handle *ion_handle_get_by_id_nolock(struct ion_client *client,
 	return ERR_PTR(-EINVAL);
 }
 
-static bool ion_handle_validate(struct ion_client *client,
-				struct ion_handle *handle)
+bool ion_handle_validate(struct ion_client *client,
+			 struct ion_handle *handle)
 {
 	WARN_ON(!mutex_is_locked(&client->lock));
 	return idr_find(&client->idr, handle->id) == handle;
diff --git a/drivers/staging/android/ion/ion_priv.h b/drivers/staging/android/ion/ion_priv.h
index 760e41885448a..e1dd25eab1dbd 100644
--- a/drivers/staging/android/ion/ion_priv.h
+++ b/drivers/staging/android/ion/ion_priv.h
@@ -149,6 +149,7 @@ struct ion_client {
  */
 struct ion_handle {
 	struct kref ref;
+	unsigned int user_ref_count;
 	struct ion_client *client;
 	struct ion_buffer *buffer;
 	struct rb_node node;
@@ -459,6 +460,9 @@ int ion_sync_for_device(struct ion_client *client, int fd);
 struct ion_handle *ion_handle_get_by_id_nolock(struct ion_client *client,
 						int id);
 
+bool ion_handle_validate(struct ion_client *client,
+			 struct ion_handle *handle);
+
 void ion_free_nolock(struct ion_client *client, struct ion_handle *handle);
 
 int ion_handle_put_nolock(struct ion_handle *handle);
-- 
2.35.0.rc0.227.g00780c9af4-goog


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH v2 4.9 3/3] ion: Do not 'put' ION handle until after its final use
  2022-01-25 14:18 [PATCH v2 4.9 1/3] ion: Fix use after free during ION_IOC_ALLOC Lee Jones
  2022-01-25 14:18 ` [PATCH v2 4.9 2/3] ion: Protect kref from userspace manipulation Lee Jones
@ 2022-01-25 14:18 ` Lee Jones
  2022-01-27 15:38 ` [PATCH v2 4.9 1/3] ion: Fix use after free during ION_IOC_ALLOC Greg KH
  2 siblings, 0 replies; 4+ messages in thread
From: Lee Jones @ 2022-01-25 14:18 UTC (permalink / raw)
  To: lee.jones; +Cc: stable

pass_to_user() eventually calls kref_put() on an ION handle which is
still live, potentially allowing for it to be legitimately freed by
the client.

Prevent this from happening before its final use in both ION_IOC_ALLOC
and ION_IOC_IMPORT.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---

NB: Only v4.9 is affected by these issues due to refactoring.

 drivers/staging/android/ion/ion-ioctl.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/staging/android/ion/ion-ioctl.c b/drivers/staging/android/ion/ion-ioctl.c
index d47e9b4171e28..a27865b94416b 100644
--- a/drivers/staging/android/ion/ion-ioctl.c
+++ b/drivers/staging/android/ion/ion-ioctl.c
@@ -165,10 +165,9 @@ long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 				     data.allocation.flags, true);
 		if (IS_ERR(handle))
 			return PTR_ERR(handle);
-		pass_to_user(handle);
 		data.allocation.handle = handle->id;
-
 		cleanup_handle = handle;
+		pass_to_user(handle);
 		break;
 	}
 	case ION_IOC_FREE:
@@ -212,11 +211,12 @@ long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 		if (IS_ERR(handle)) {
 			ret = PTR_ERR(handle);
 		} else {
+			data.handle.handle = handle->id;
 			handle = pass_to_user(handle);
-			if (IS_ERR(handle))
+			if (IS_ERR(handle)) {
 				ret = PTR_ERR(handle);
-			else
-				data.handle.handle = handle->id;
+				data.handle.handle = 0;
+			}
 		}
 		break;
 	}
-- 
2.35.0.rc0.227.g00780c9af4-goog


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH v2 4.9 1/3] ion: Fix use after free during ION_IOC_ALLOC
  2022-01-25 14:18 [PATCH v2 4.9 1/3] ion: Fix use after free during ION_IOC_ALLOC Lee Jones
  2022-01-25 14:18 ` [PATCH v2 4.9 2/3] ion: Protect kref from userspace manipulation Lee Jones
  2022-01-25 14:18 ` [PATCH v2 4.9 3/3] ion: Do not 'put' ION handle until after its final use Lee Jones
@ 2022-01-27 15:38 ` Greg KH
  2 siblings, 0 replies; 4+ messages in thread
From: Greg KH @ 2022-01-27 15:38 UTC (permalink / raw)
  To: Lee Jones; +Cc: stable, Daniel Rosenberg, Dennis Cagle, Patrick Daly

On Tue, Jan 25, 2022 at 02:18:06PM +0000, Lee Jones wrote:
> From: Daniel Rosenberg <drosen@google.com>
> 
> If a user happens to call ION_IOC_FREE during an ION_IOC_ALLOC
> on the just allocated id, and the copy_to_user fails, the cleanup
> code will attempt to free an already freed handle.
> 
> This adds a wrapper for ion_alloc that adds an ion_handle_get to
> avoid this.
> 
> Signed-off-by: Daniel Rosenberg <drosen@google.com>
> Signed-off-by: Dennis Cagle <d-cagle@codeaurora.org>
> Signed-off-by: Patrick Daly <pdaly@codeaurora.org>
> Signed-off-by: Lee Jones <lee.jones@linaro.org>
> ---
> 
> NB: These are Android patches that were not sent to Mainline.
> 
> Only v4.9 is affected by these issues due to refactoring.

All now queued up, thanks.

greg k-h

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2022-01-27 15:38 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-25 14:18 [PATCH v2 4.9 1/3] ion: Fix use after free during ION_IOC_ALLOC Lee Jones
2022-01-25 14:18 ` [PATCH v2 4.9 2/3] ion: Protect kref from userspace manipulation Lee Jones
2022-01-25 14:18 ` [PATCH v2 4.9 3/3] ion: Do not 'put' ION handle until after its final use Lee Jones
2022-01-27 15:38 ` [PATCH v2 4.9 1/3] ion: Fix use after free during ION_IOC_ALLOC Greg KH

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).