Skip to content

Commit a7544fd

Browse files
euntaikgregkh
authored andcommitted
staging/android/ion : fix a race condition in the ion driver
commit 9590232bb4f4cc824f3425a6e1349afbe6d6d2b7 upstream. There is a use-after-free problem in the ion driver. This is caused by a race condition in the ion_ioctl() function. A handle has ref count of 1 and two tasks on different cpus calls ION_IOC_FREE simultaneously. cpu 0 cpu 1 ------------------------------------------------------- ion_handle_get_by_id() (ref == 2) ion_handle_get_by_id() (ref == 3) ion_free() (ref == 2) ion_handle_put() (ref == 1) ion_free() (ref == 0 so ion_handle_destroy() is called and the handle is freed.) ion_handle_put() is called and it decreases the slub's next free pointer The problem is detected as an unaligned access in the spin lock functions since it uses load exclusive instruction. In some cases it corrupts the slub's free pointer which causes a mis-aligned access to the next free pointer.(kmalloc returns a pointer like ffffc0745b4580aa). And it causes lots of other hard-to-debug problems. This symptom is caused since the first member in the ion_handle structure is the reference count and the ion driver decrements the reference after it has been freed. To fix this problem client->lock mutex is extended to protect all the codes that uses the handle. Signed-off-by: Eun Taik Lee <eun.taik.lee@samsung.com> Reviewed-by: Laura Abbott <labbott@redhat.com> Cc: Ben Hutchings <ben.hutchings@codethink.co.uk> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> index 7ff2a7ec871f..33b390e7ea31
1 parent d23ef85 commit a7544fd

1 file changed

Lines changed: 42 additions & 13 deletions

File tree

  • drivers/staging/android/ion

drivers/staging/android/ion/ion.c

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -387,13 +387,22 @@ static void ion_handle_get(struct ion_handle *handle)
387387
kref_get(&handle->ref);
388388
}
389389

390-
static int ion_handle_put(struct ion_handle *handle)
390+
static int ion_handle_put_nolock(struct ion_handle *handle)
391+
{
392+
int ret;
393+
394+
ret = kref_put(&handle->ref, ion_handle_destroy);
395+
396+
return ret;
397+
}
398+
399+
int ion_handle_put(struct ion_handle *handle)
391400
{
392401
struct ion_client *client = handle->client;
393402
int ret;
394403

395404
mutex_lock(&client->lock);
396-
ret = kref_put(&handle->ref, ion_handle_destroy);
405+
ret = ion_handle_put_nolock(handle);
397406
mutex_unlock(&client->lock);
398407

399408
return ret;
@@ -417,20 +426,30 @@ static struct ion_handle *ion_handle_lookup(struct ion_client *client,
417426
return ERR_PTR(-EINVAL);
418427
}
419428

420-
static struct ion_handle *ion_handle_get_by_id(struct ion_client *client,
429+
static struct ion_handle *ion_handle_get_by_id_nolock(struct ion_client *client,
421430
int id)
422431
{
423432
struct ion_handle *handle;
424433

425-
mutex_lock(&client->lock);
426434
handle = idr_find(&client->idr, id);
427435
if (handle)
428436
ion_handle_get(handle);
429-
mutex_unlock(&client->lock);
430437

431438
return handle ? handle : ERR_PTR(-EINVAL);
432439
}
433440

441+
struct ion_handle *ion_handle_get_by_id(struct ion_client *client,
442+
int id)
443+
{
444+
struct ion_handle *handle;
445+
446+
mutex_lock(&client->lock);
447+
handle = ion_handle_get_by_id_nolock(client, id);
448+
mutex_unlock(&client->lock);
449+
450+
return handle;
451+
}
452+
434453
static bool ion_handle_validate(struct ion_client *client,
435454
struct ion_handle *handle)
436455
{
@@ -532,22 +551,28 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
532551
}
533552
EXPORT_SYMBOL(ion_alloc);
534553

535-
void ion_free(struct ion_client *client, struct ion_handle *handle)
554+
static void ion_free_nolock(struct ion_client *client, struct ion_handle *handle)
536555
{
537556
bool valid_handle;
538557

539558
BUG_ON(client != handle->client);
540559

541-
mutex_lock(&client->lock);
542560
valid_handle = ion_handle_validate(client, handle);
543561

544562
if (!valid_handle) {
545563
WARN(1, "%s: invalid handle passed to free.\n", __func__);
546-
mutex_unlock(&client->lock);
547564
return;
548565
}
566+
ion_handle_put_nolock(handle);
567+
}
568+
569+
void ion_free(struct ion_client *client, struct ion_handle *handle)
570+
{
571+
BUG_ON(client != handle->client);
572+
573+
mutex_lock(&client->lock);
574+
ion_free_nolock(client, handle);
549575
mutex_unlock(&client->lock);
550-
ion_handle_put(handle);
551576
}
552577
EXPORT_SYMBOL(ion_free);
553578

@@ -1283,11 +1308,15 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
12831308
{
12841309
struct ion_handle *handle;
12851310

1286-
handle = ion_handle_get_by_id(client, data.handle.handle);
1287-
if (IS_ERR(handle))
1311+
mutex_lock(&client->lock);
1312+
handle = ion_handle_get_by_id_nolock(client, data.handle.handle);
1313+
if (IS_ERR(handle)) {
1314+
mutex_unlock(&client->lock);
12881315
return PTR_ERR(handle);
1289-
ion_free(client, handle);
1290-
ion_handle_put(handle);
1316+
}
1317+
ion_free_nolock(client, handle);
1318+
ion_handle_put_nolock(handle);
1319+
mutex_unlock(&client->lock);
12911320
break;
12921321
}
12931322
case ION_IOC_SHARE:

0 commit comments

Comments
 (0)