Skip to content

Commit

Permalink
staging: android: ion: fix ION_IOC_{MAP,SHARE} use-after-free
Browse files Browse the repository at this point in the history
commit 2c155709e4ef2d86d0176aac82e44c048a7e0255 upstream.

The ION_IOC_{MAP,SHARE} ioctls drop and reacquire client->lock several
times while operating on one of the client's ion_handles.  This creates
windows where userspace can call ION_IOC_FREE on the same client with
the same handle, and effectively make the kernel drop its own reference.
For example:

- thread A: ION_IOC_ALLOC creates an ion_handle with refcount 1
- thread A: starts ION_IOC_MAP and increments the refcount to 2
- thread B: ION_IOC_FREE decrements the refcount to 1
- thread B: ION_IOC_FREE decrements the refcount to 0 and frees the
            handle
- thread A: continues ION_IOC_MAP with a dangling ion_handle * to
            freed memory

Fix this by holding client->lock for the duration of
ION_IOC_{MAP,SHARE}, preventing the concurrent ION_IOC_FREE.  Also
remove ion_handle_get_by_id(), since there's literally no way to use it
safely.

This patch is applied on top of 4.4.y, and applies to older kernels
too.  4.9.y was fixed separately.  Kernels 4.12 and later are
unaffected, since all the underlying ion_handle infrastructure has been
ripped out.

Cc: stable@vger.kernel.org # v4.4-
Signed-off-by: Greg Hackmann <ghackmann@google.com>
Acked-by: Laura Abbott <labbott@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
greghackmann authored and gregkh committed Oct 13, 2018
1 parent be6f6ea commit d82ad70
Showing 1 changed file with 37 additions and 23 deletions.
60 changes: 37 additions & 23 deletions drivers/staging/android/ion/ion.c
Original file line number Diff line number Diff line change
Expand Up @@ -451,18 +451,6 @@ static struct ion_handle *ion_handle_get_by_id_nolock(struct ion_client *client,
return ERR_PTR(-EINVAL);
}

struct ion_handle *ion_handle_get_by_id(struct ion_client *client,
int id)
{
struct ion_handle *handle;

mutex_lock(&client->lock);
handle = ion_handle_get_by_id_nolock(client, id);
mutex_unlock(&client->lock);

return handle;
}

static bool ion_handle_validate(struct ion_client *client,
struct ion_handle *handle)
{
Expand Down Expand Up @@ -1138,23 +1126,27 @@ static struct dma_buf_ops dma_buf_ops = {
.kunmap = ion_dma_buf_kunmap,
};

struct dma_buf *ion_share_dma_buf(struct ion_client *client,
struct ion_handle *handle)
static struct dma_buf *__ion_share_dma_buf(struct ion_client *client,
struct ion_handle *handle,
bool lock_client)
{
struct ion_buffer *buffer;
struct dma_buf *dmabuf;
bool valid_handle;

mutex_lock(&client->lock);
if (lock_client)
mutex_lock(&client->lock);
valid_handle = ion_handle_validate(client, handle);
if (!valid_handle) {
WARN(1, "%s: invalid handle passed to share.\n", __func__);
mutex_unlock(&client->lock);
if (lock_client)
mutex_unlock(&client->lock);
return ERR_PTR(-EINVAL);
}
buffer = handle->buffer;
ion_buffer_get(buffer);
mutex_unlock(&client->lock);
if (lock_client)
mutex_unlock(&client->lock);

dmabuf = dma_buf_export(buffer, &dma_buf_ops, buffer->size, O_RDWR,
NULL);
Expand All @@ -1165,14 +1157,21 @@ struct dma_buf *ion_share_dma_buf(struct ion_client *client,

return dmabuf;
}

struct dma_buf *ion_share_dma_buf(struct ion_client *client,
struct ion_handle *handle)
{
return __ion_share_dma_buf(client, handle, true);
}
EXPORT_SYMBOL(ion_share_dma_buf);

int ion_share_dma_buf_fd(struct ion_client *client, struct ion_handle *handle)
static int __ion_share_dma_buf_fd(struct ion_client *client,
struct ion_handle *handle, bool lock_client)
{
struct dma_buf *dmabuf;
int fd;

dmabuf = ion_share_dma_buf(client, handle);
dmabuf = __ion_share_dma_buf(client, handle, lock_client);
if (IS_ERR(dmabuf))
return PTR_ERR(dmabuf);

Expand All @@ -1182,8 +1181,19 @@ int ion_share_dma_buf_fd(struct ion_client *client, struct ion_handle *handle)

return fd;
}

int ion_share_dma_buf_fd(struct ion_client *client, struct ion_handle *handle)
{
return __ion_share_dma_buf_fd(client, handle, true);
}
EXPORT_SYMBOL(ion_share_dma_buf_fd);

static int ion_share_dma_buf_fd_nolock(struct ion_client *client,
struct ion_handle *handle)
{
return __ion_share_dma_buf_fd(client, handle, false);
}

struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd)
{
struct dma_buf *dmabuf;
Expand Down Expand Up @@ -1330,11 +1340,15 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct ion_handle *handle;

handle = ion_handle_get_by_id(client, data.handle.handle);
if (IS_ERR(handle))
mutex_lock(&client->lock);
handle = ion_handle_get_by_id_nolock(client, data.handle.handle);
if (IS_ERR(handle)) {
mutex_unlock(&client->lock);
return PTR_ERR(handle);
data.fd.fd = ion_share_dma_buf_fd(client, handle);
ion_handle_put(handle);
}
data.fd.fd = ion_share_dma_buf_fd_nolock(client, handle);
ion_handle_put_nolock(handle);
mutex_unlock(&client->lock);
if (data.fd.fd < 0)
ret = data.fd.fd;
break;
Expand Down

0 comments on commit d82ad70

Please sign in to comment.