[In-The-Wild] CVE-2024-43047 : Qualcomm DSP Service Use-After-Free lead to Elevation of Privilege Vulnerability

Summary:

Technical Details:

Overview:

Analysis:

Code Flow:

Core Issues

Exploitation Flow:

  1. Get valid fd of active mapping:
    • Create legitimate FastRPC call
    • Obtain fd of memory mapping
  2. Manipulate header buffer:
    • Locate fdlist in header buffer
    • Inject valid fd value
  3. Trigger put_args:
    • System processes fdlist
    • Finds legitimate mapping
    • Prematurely frees mapping
  4. UAF condition:
    • DSP continues to use freed mapping
    • Memory corruption occurs

Patch:

diff --git a/dsp/adsprpc.c b/dsp/adsprpc.c
index 6ec85443eb44153ae3e89c2d4d8d09af8672eb93..8cfb8e6f28b8629bc4d7171483a06a02a854cc4f 100644
--- a/dsp/adsprpc.c
+++ b/dsp/adsprpc.c
@@ -955,6 +955,8 @@ static int fastrpc_mmap_remove(struct fastrpc_file *fl, int fd, uintptr_t va,
 			map->refs == 1 &&
 			/* Remove map only if it isn't being used in any pending RPC calls */
 			!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;
@@ -994,8 +996,9 @@ 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->is_persistent && !map->ctx_refs)
+		if (map->refs)
+			map->refs--;
+		if (!map->refs && !map->is_persistent)
 			hlist_del_init(&map->hn);
 		if (map->refs > 0) {
 			ADSPRPC_WARN(
@@ -1008,10 +1011,14 @@ static void fastrpc_mmap_free(struct fastrpc_mmap *map, uint32_t flags)
 			map->in_use = false;
 		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 (flags || (!map->refs && !map->ctx_refs && !map->dma_handle_refs))
 			hlist_del_init(&map->hn);
-		if (map->refs > 0 && !flags)
+		else
 			return;
 	}
 	if (map->flags == ADSP_MMAP_HEAP_ADDR ||
@@ -2501,21 +2508,22 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx)
 		if (err) {
 			for (j = bufs; j < i; j++) {
 				/*
-				 * Due to error decrement ctx refs count before mmap free
+				 * Due to error decrement  refs count before mmap free
 				 * for each in/out handle, if map created
 				 */
-				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;
 		} else if (ctx->maps[i]) {
 			/*
-			 * Increment ctx refs count for in/out handle if map created
+			 * Increment  refs count for in/out handle if map created
 			 * and no error, indicate map under use in remote call
 			 */
-			ctx->maps[i]->ctx_refs++;
+			ctx->maps[i]->dma_handle_refs++;
 		}
 		ipage += 1;
 	}
@@ -2667,14 +2675,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];
+		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;
+				}
 
-		if (map) {
-			pages[i].addr = map->phys;
-			pages[i].size = map->size;
+			} else {
+				/* map already freed by some other call */
+				mutex_unlock(&ctx->fl->map_mutex);
+				ADSPRPC_ERR("could not find map associated with dma hadle 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 */
@@ -2883,9 +2910,10 @@ static int put_args(uint32_t kernel, struct smq_invoke_ctx *ctx,
 			 * Decrement ctx refs count before mmap free,
 			 * indicate remote call no longer using it
 			 */
-			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);
diff --git a/dsp/adsprpc_shared.h b/dsp/adsprpc_shared.h
index cd8ed472ff39a33fed610f17b3265d11f7167b97..0b773fd74e1cc51150e5d3b7afeb6986f16da3d1 100644
--- a/dsp/adsprpc_shared.h
+++ b/dsp/adsprpc_shared.h
@@ -794,6 +794,8 @@ struct fastrpc_mmap {
 	char *servloc_name;			/* Indicate which daemon mapped this */
 	/* Indicates map is being used by a pending RPC call */
 	unsigned int ctx_refs;
+	/* Map in use for dma handle */
+	unsigned int dma_handle_refs;
 };
 
 enum fastrpc_perfkeys {

Proof-of-Concept:

TBA