skip to content
Back to
Home Bounties CodeQL Research Advisories Get Involved Events
March 8, 2021

GHSL-2020-375: Use-after-free (UaF) in Qualcomm kgsl driver - CVE-2020-11239

Man Yue Mo

Coordinated Disclosure Timeline


Use-after-free in kgsl_ioctl_gpuobj_import and kgsl_ioctl_map_user_mem of the Qualcomm kgsl driver


msm kernel

Tested Version

Pixel 4 QQ3A.200705.002 build


The code references here is in the coral-4.14 branch of the kernel. The use-after-free issue seems to only affect this branch (after the new ION ABI is introduced in 4.12). In the kgsl_ioctl_gpuobj_import function, if the parameter type is set to KGSL_USER_MEM_TYPE_ADDR, then the function _gpuobj_map_useraddr will be used to create the memory mapping [1]. This function will call kgsl_setup_useraddr [2] and try to create a mapping with dma first [3]. If a valid dma buffer is found, it will then use it to create the mapping, and attach the dma buffer to the device [4], [5]

static int kgsl_setup_dma_buf(struct kgsl_device *device,
                                struct kgsl_pagetable *pagetable,
                                struct kgsl_mem_entry *entry,
                                struct dma_buf *dmabuf)
        int ret = 0;
        struct scatterlist *s;
        struct sg_table *sg_table;
        struct dma_buf_attachment *attach = NULL;
        struct kgsl_dma_buf_meta *meta;
        meta = kzalloc(sizeof(*meta), GFP_KERNEL);
        if (!meta)
                return -ENOMEM;
        attach = dma_buf_attach(dmabuf, device->dev);  //<------- a.
        sg_table = dma_buf_map_attachment(attach, DMA_TO_DEVICE);
        meta->table = sg_table;
        entry->priv_data = meta;
        entry->memdesc.sgt = sg_table;           //<------- b.

This will create a sg_table for the attachment by duplicating the one from the dma_buf (a. and [6], see below)

The ion implementation of dma_buf_attach in (a.) is as follows:

static int ion_dma_buf_attach(struct dma_buf *dmabuf, struct device *dev,
                                struct dma_buf_attachment *attachment)
        table = dup_sg_table(buffer->sg_table);
        a->table = table;                          //<---- c. duplicated table stored in attachment, which is the output of dma_buf_attach in a.
        list_add(&a->list, &buffer->attachments);  //<---- d. attachment got added to dma_buf::attachments
        return 0;

This stores the duplicated table in an ion_dma_buf_attachment as raw pointer, while at the same time, the table is also stored in entry->memdesc.sgt in (b) and the ion_dma_buf_attachment is also stored in the attachment list of dma_buf.

If the ioctl call then failed at the kgsl_mem_entry_attach_process call, it will go to unmap [7]
        if (param->type == KGSL_USER_MEM_TYPE_DMABUF) {
                entry->memdesc.sgt = NULL;
        kgsl_sharedmem_free(&entry->memdesc);  //<---- deletes |table| in c.

As param->type in this case is KGSL_USER_MEM_TYPE_ADDR, kgsl_destroy_ion will not be called, which means that the dma_buf will remain attached to the gpu, and more importantly, the ion_dma_buf_attachment created in ion_dma_buf_attach will remain in the attachment list of the buffer. Now kgsl_sharedmem_free that follows will delete memdesc.sgt, which is the same as table in (c):

void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc)
        if (memdesc->sgt) {

After this point, the dma_buf, which the user holds a reference to and can call its ioctl at any time, will contain a reference to an ion_dma_buf_attachment in its attachments list, and this attachment holds a reference to a free’d sg_table. A use-after-free can then be triggered, for example, by using the DMA_BUF_IOCTL_SYNC ioctl call on this dma_buf, which is implemented by __ion_dma_buf_begin_cpu_access:

static int __ion_dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
                                          enum dma_data_direction direction,
                                          bool sync_only_mapped)
        list_for_each_entry(a, &buffer->attachments, list) {
                if (sync_only_mapped)
                        tmp = ion_sgl_sync_mapped(a->dev, a->table->sgl,        //<--- use-after-free of a->table
                                                  direction, true);
                        dma_sync_sg_for_cpu(a->dev, a->table->sgl,              //<--- use-after-free of a->table
                                            a->table->nents, direction);

The kgsl_ioctl_map_user_mem system call also has a similar problem.



Can be exploited from the application sandbox to achieve arbitrary kernel code execution in many devices.


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


You can contact the GHSL team at, please include the GHSL-2020-375 in any communication regarding this issue.