msm: adsprpc: Handle UAF in fastrpc internal munmap

Added reference count for contex map indicate memory under used
in remote call. And, this memory would not removed in internal
unmap to avoid UAF.

Change-Id: Ieb4ff6b298ff9c48953bc5b3539fdfe19a14b442
Acked-by: Santosh Sakore <ssakore@qti.qualcomm.com>
Signed-off-by: Santosh Sakore <quic_ssakore@quicinc.com>
This commit is contained in:
Santosh Sakore 2023-03-23 16:44:49 +05:30
commit 96507bd863

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
*/
/* Uncomment this block to log an error on every VERIFY failure */
@ -579,10 +579,11 @@ struct fastrpc_mmap {
bool is_persistent; /* Indicates whether map is persistent */
int frpc_md_index; /* Minidump unique index */
uintptr_t attr;
bool in_use; /* Indicates if persistent map is in use*/
bool in_use; /* Indicates if persistent map is in use */
struct timespec64 map_start_time;
struct timespec64 map_end_time;
bool is_filemap; /*flag to indicate map used in process init*/
bool is_filemap; /* flag to indicate map used in process init */
unsigned int ctx_refs; /* Indicates reference count for context map */
};
enum fastrpc_perfkeys {
@ -1220,7 +1221,7 @@ static int fastrpc_mmap_remove(struct fastrpc_file *fl, int fd, uintptr_t va,
hlist_for_each_entry_safe(map, n, &me->maps, hn) {
if (map->refs == 1 && map->raddr == va &&
map->raddr + map->len == va + len &&
/*Remove map if not used in process initialization*/
/* Remove map if not used in process initialization */
!map->is_filemap) {
match = map;
hlist_del_init(&map->hn);
@ -1233,9 +1234,10 @@ static int fastrpc_mmap_remove(struct fastrpc_file *fl, int fd, uintptr_t va,
return 0;
}
hlist_for_each_entry_safe(map, n, &fl->maps, hn) {
if (map->refs == 1 && map->raddr == va &&
map->raddr + map->len == va + len &&
/*Remove map if not used in process initialization*/
/* Remove if only one reference map and no context map */
if (map->refs == 1 && !map->ctx_refs &&
map->raddr == va && map->raddr + map->len == va + len &&
/* Remove map if not used in process initialization */
!map->is_filemap) {
match = map;
hlist_del_init(&map->hn);
@ -1274,7 +1276,7 @@ static void fastrpc_mmap_free(struct fastrpc_mmap *map, uint32_t flags)
map->flags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
spin_lock(&me->hlock);
map->refs--;
if (!map->refs && !map->is_persistent)
if (!map->refs && !map->is_persistent && !map->ctx_refs)
hlist_del_init(&map->hn);
spin_unlock(&me->hlock);
if (map->refs > 0) {
@ -1290,7 +1292,7 @@ static void fastrpc_mmap_free(struct fastrpc_mmap *map, uint32_t flags)
}
} else {
map->refs--;
if (!map->refs)
if (!map->refs && !map->ctx_refs)
hlist_del_init(&map->hn);
if (map->refs > 0 && !flags)
return;
@ -1426,6 +1428,7 @@ static int fastrpc_mmap_create(struct fastrpc_file *fl, int fd,
map->attr = attr;
map->frpc_md_index = -1;
map->is_filemap = false;
map->ctx_refs = 0;
ktime_get_real_ts64(&map->map_start_time);
if (mflags == ADSP_MMAP_HEAP_ADDR ||
mflags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
@ -2177,8 +2180,11 @@ static void context_free(struct smq_invoke_ctx *ctx)
spin_unlock(&ctx->fl->hlock);
mutex_lock(&ctx->fl->map_mutex);
for (i = 0; i < nbufs; ++i)
for (i = 0; i < nbufs; ++i) {
if (ctx->maps[i] && ctx->maps[i]->ctx_refs)
ctx->maps[i]->ctx_refs--;
fastrpc_mmap_free(ctx->maps[i], 0);
}
mutex_unlock(&ctx->fl->map_mutex);
fastrpc_buf_free(ctx->buf, 1);
@ -2485,6 +2491,8 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx)
err = fastrpc_mmap_create(ctx->fl, ctx->fds[i],
ctx->attrs[i], buf, len,
mflags, &ctx->maps[i]);
if (ctx->maps[i])
ctx->maps[i]->ctx_refs++;
mutex_unlock(&ctx->fl->map_mutex);
if (err)
goto bail;
@ -2502,9 +2510,14 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx)
err = fastrpc_mmap_create(ctx->fl, ctx->fds[i],
FASTRPC_ATTR_NOVA, 0, 0, dmaflags,
&ctx->maps[i]);
if (!err && ctx->maps[i])
ctx->maps[i]->ctx_refs++;
if (err) {
for (j = bufs; j < i; j++)
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);
}
mutex_unlock(&ctx->fl->map_mutex);
goto bail;
}
@ -2835,6 +2848,8 @@ static int put_args(uint32_t kernel, struct smq_invoke_ctx *ctx,
}
} else {
mutex_lock(&ctx->fl->map_mutex);
if (ctx->maps[i]->ctx_refs)
ctx->maps[i]->ctx_refs--;
fastrpc_mmap_free(ctx->maps[i], 0);
mutex_unlock(&ctx->fl->map_mutex);
ctx->maps[i] = NULL;
@ -2845,8 +2860,11 @@ static int put_args(uint32_t kernel, struct smq_invoke_ctx *ctx,
if (!fdlist[i])
break;
if (!fastrpc_mmap_find(ctx->fl, (int)fdlist[i], 0, 0,
0, 0, &mmap))
0, 0, &mmap)) {
if (mmap && mmap->ctx_refs)
mmap->ctx_refs--;
fastrpc_mmap_free(mmap, 0);
}
}
mutex_unlock(&ctx->fl->map_mutex);
if (ctx->crc && crclist && rpra)