Coordinated Disclosure Timeline

Summary

Imported memory from user space can be accessed after it has been freed

Product

Arm Mali

Tested Version

x86_64 VM built with Mali driver version r40p0 and Pixel 6 with Patch level: November 2022 Android 13.

Details

Issue: Freed imported user memory can be accessed via vmap in the Arm Mali GPU driver (GHSL-2022-127)

When importing memory of the type BASE_MEM_IMPORT_TYPE_USER_BUFFER and the KBASE_REG_SHARE_BOTH flag is not passed, pages from user space will not be pinned during the creation of the memory region. In this case, the backing pages of the region are pinned when the BASE_JD_REQ_EXTERNAL_RESOURCES gpu soft job is submitted, during which the user space pages that form the backing pages of the region will have their references increased until the gpu job is completed.

After the backing pages are pinned, the KBASE_IOCTL_STICKY_RESOURCE_MAP ioctl can be used to call kbase_map_external_resource to increase the current_mapping_usage_count of the kbase_alloc_import_user_buf of the memory region. Then when the BASE_JD_REQ_EXTERNAL_RESOURCES completes, it’ll decrease the current_mapping_usage_count by one, instead of dropping its references to the backing pages.

After this, the backing pages can then be freed by using the KBASE_IOCTL_STICKY_RESOURCE_UNMAP ioctl, which is not protected by the kbase_jd_context::lock. There are various gpu softjobs where a user supplied gpu address gets vmap and written to, some of these are protected only by the kbase_jd_context::lock. For example, in kbase_jit_allocate_process:

static int kbase_jit_allocate_process(struct kbase_jd_atom *katom)
{
    ...
for (i = 0, info = katom->softjob_data; i < count; i++, info++) {
u64 entry_mmu_flags = 0;
/*
* Write the address of the JIT allocation to the user provided
* GPU allocation.
*/
ptr = kbase_vmap_prot(kctx, info->gpu_alloc_addr, sizeof(*ptr),
KBASE_REG_CPU_WR, &mapping);                           //<------- 1.
        ...
new_addr = reg->start_pfn << PAGE_SHIFT;
*ptr = new_addr;                                               //<------- 2.
        ...
kbase_vunmap(kctx, &mapping);
}

In the above, info->gpu_alloc_addr in 1. is provided by the user, which is vmapped and gets written to in 2. In particular, the user can supply the address of the backing page of a BASE_MEM_IMPORT_TYPE_USER_BUFFER as the gpu_alloc_addr.

As the code region between 1. and 2. is only protected by the kbase_jd_context::lock, it is possible to have this softjob race with the KBASE_IOCTL_STICKY_RESOURCE_UNMAP ioctl which drops the reference to the backing page of gpu_alloc_addr between 1. and 2. in the above. This then causes the backing page to be freed and 2. will then write to memory that is already freed.

Impact

Allows read and write access to already freed memory, possibly enabling an attacker to gain code execution in the kernel.

CVE

Credit

This issue was discovered and reported by GHSL team member @m-y-mo (Man Yue Mo).

Contact

You can contact the GHSL team at securitylab@github.com, please include a reference to GHSL-2022-127 in any communication regarding this issue.