Skip to content

Commit

Permalink
ANDROID: ion: Protect kref from userspace manipulation
Browse files Browse the repository at this point in the history
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.

Bug: 34276203

Change-Id: Ibc36bc4405788ed0fea7337b541cad3be2b934c0
Signed-off-by: Daniel Rosenberg <drosen@google.com>
Git-repo: https://android.googlesource.com/kernel/msm/
Git-commit: 20abfcc16884a5af973a5e91dd013ddd789c44f4
[d-cagle@codeaurora.org: Resolve style issues]
Signed-off-by: Dennis Cagle <d-cagle@codeaurora.org>
  • Loading branch information
drosen-google authored and mdmower committed Oct 22, 2017
1 parent 2867fe2 commit 01c985f
Showing 1 changed file with 76 additions and 6 deletions.
82 changes: 76 additions & 6 deletions drivers/gpu/ion/ion.c
Expand Up @@ -108,6 +108,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;
Expand Down Expand Up @@ -399,6 +400,48 @@ int ion_handle_put(struct ion_handle *handle)
return ret;
}

/* 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 struct ion_handle *ion_handle_lookup(struct ion_client *client,
struct ion_buffer *buffer)
{
Expand Down Expand Up @@ -618,6 +661,25 @@ static void ion_free_nolock(struct ion_client *client, struct ion_handle *handle
ion_handle_put_nolock(handle);
}

/* Must hold the client lock */
static void user_ion_free_nolock(struct ion_client *client, struct ion_handle *handle)
{
bool valid_handle;

BUG_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);
}

void ion_free(struct ion_client *client, struct ion_handle *handle)
{
BUG_ON(client != handle->client);
Expand Down Expand Up @@ -1387,11 +1449,14 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (IS_ERR(handle))
return PTR_ERR(handle);

pass_to_user(handle);
data.handle = (ion_user_handle_t)handle->id;

if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
ion_free(client, handle);
ion_handle_put(handle);
mutex_lock(&client->lock);
user_ion_free_nolock(client, handle);
ion_handle_put_nolock(handle);
mutex_unlock(&client->lock);
return -EFAULT;
}
ion_handle_put(handle);
Expand All @@ -1412,7 +1477,7 @@ static 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;
Expand Down Expand Up @@ -1445,10 +1510,15 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
sizeof(struct ion_fd_data)))
return -EFAULT;
handle = ion_import_dma_buf(client, data.fd);
if (IS_ERR(handle))
if (IS_ERR(handle)) {
ret = PTR_ERR(handle);
else
data.handle = (ion_user_handle_t)handle->id;
} else {
handle = pass_to_user(handle);
if (IS_ERR(handle))
ret = PTR_ERR(handle);
else
data.handle = (ion_user_handle_t)handle->id;
}

if (copy_to_user((void __user *)arg, &data,
sizeof(struct ion_fd_data)))
Expand Down

0 comments on commit 01c985f

Please sign in to comment.