Merge "adsprpc: Handle UAF scenario in put_args"

This commit is contained in:
QCTECMDR Service 2024-08-30 07:44:18 -07:00 committed by Gerrit - the friendly Code Review server
commit aee2927ae5

View File

@ -631,6 +631,8 @@ struct fastrpc_mmap {
struct timespec64 map_end_time;
bool is_filemap; /* flag to indicate map used in process init */
unsigned int ctx_refs; /* Indicates reference count for context map */
/* Map in use for dma handle */
unsigned int dma_handle_refs;
};
enum fastrpc_perfkeys {
@ -1306,6 +1308,8 @@ static int fastrpc_mmap_remove(struct fastrpc_file *fl, int fd, uintptr_t va,
map->refs == 1 &&
/* Remove if only one reference map and no context map */
!map->ctx_refs &&
/* Remove map only if it isn't being used by DSP */
!map->dma_handle_refs &&
/* Skip unmap if it is fastrpc shell memory */
!map->is_filemap) {
match = map;
@ -1345,7 +1349,8 @@ static void fastrpc_mmap_free(struct fastrpc_mmap *map, uint32_t flags)
if (map->flags == ADSP_MMAP_HEAP_ADDR ||
map->flags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
spin_lock_irqsave(&me->hlock, irq_flags);
map->refs--;
if (map->refs)
map->refs--;
if (!map->refs && !map->ctx_refs)
hlist_del_init(&map->hn);
if (map->refs > 0) {
@ -1357,8 +1362,12 @@ static void fastrpc_mmap_free(struct fastrpc_mmap *map, uint32_t flags)
}
spin_unlock_irqrestore(&me->hlock, irq_flags);
} else {
map->refs--;
if (!map->refs && !map->ctx_refs)
if (map->refs)
map->refs--;
/* flags is passed as 1 during fastrpc_file_free (ie process exit),
* so that maps will be cleared even though references are present.
*/
if (!map->refs && !map->ctx_refs && !map->dma_handle_refs)
hlist_del_init(&map->hn);
if (map->refs > 0 && !flags)
return;
@ -2664,12 +2673,13 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx)
FASTRPC_ATTR_NOVA, 0, 0, dmaflags,
&ctx->maps[i]);
if (!err && ctx->maps[i])
ctx->maps[i]->ctx_refs++;
ctx->maps[i]->dma_handle_refs++;
if (err) {
for (j = bufs; j < i; j++) {
if (ctx->maps[j] && ctx->maps[j]->ctx_refs)
ctx->maps[j]->ctx_refs--;
fastrpc_mmap_free(ctx->maps[j], 0);
if (ctx->maps[j] && ctx->maps[j]->dma_handle_refs) {
ctx->maps[j]->dma_handle_refs--;
fastrpc_mmap_free(ctx->maps[j], 0);
}
}
mutex_unlock(&ctx->fl->map_mutex);
goto bail;
@ -2807,13 +2817,33 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx)
rpra[i].buf.pv = buf;
}
PERF_END);
/* Since we are not holidng map_mutex during get args whole time
* it is possible that dma handle map may be removed by some invalid
* fd passed by DSP. Inside the lock check if the map present or not
*/
mutex_lock(&ctx->fl->map_mutex);
for (i = bufs; i < bufs + handles; ++i) {
struct fastrpc_mmap *map = ctx->maps[i];
if (map) {
pages[i].addr = map->phys;
pages[i].size = map->size;
struct fastrpc_mmap *mmap = NULL;
/* check if map was created */
if (ctx->maps[i]) {
/* check if map still exist */
if (!fastrpc_mmap_find(ctx->fl, ctx->fds[i], NULL, 0, 0,
0, 0, &mmap)) {
if (mmap) {
pages[i].addr = mmap->phys;
pages[i].size = mmap->size;
}
} else {
/* map already freed by some other call */
mutex_unlock(&ctx->fl->map_mutex);
ADSPRPC_ERR("could not find map associated with dma handle fd %d\n",
ctx->fds[i]);
goto bail;
}
}
}
mutex_unlock(&ctx->fl->map_mutex);
fdlist = (uint64_t *)&pages[bufs + handles];
crclist = (uint32_t *)&fdlist[M_FDLIST];
/* reset fds, crc and early wakeup hint memory */
@ -3012,9 +3042,10 @@ static int put_args(uint32_t kernel, struct smq_invoke_ctx *ctx,
break;
if (!fastrpc_mmap_find(ctx->fl, (int)fdlist[i], NULL, 0, 0,
0, 0, &mmap)){
if (mmap && mmap->ctx_refs)
mmap->ctx_refs--;
fastrpc_mmap_free(mmap, 0);
if (mmap && mmap->dma_handle_refs) {
mmap->dma_handle_refs = 0;
fastrpc_mmap_free(mmap, 0);
}
}
}
mutex_unlock(&ctx->fl->map_mutex);