static const struct {
unsigned int cmd;
kgsl_ioctl_func_t func;
unsigned int flags;
} kgsl_ioctl_funcs[] = {
KGSL_IOCTL_FUNC(IOCTL_KGSL_DEVICE_GETPROPERTY,
kgsl_ioctl_device_getproperty,
KGSL_IOCTL_LOCK),
KGSL_IOCTL_FUNC(IOCTL_KGSL_DEVICE_WAITTIMESTAMP,
kgsl_ioctl_device_waittimestamp,
KGSL_IOCTL_LOCK),
KGSL_IOCTL_FUNC(IOCTL_KGSL_DEVICE_WAITTIMESTAMP_CTXTID,
kgsl_ioctl_device_waittimestamp_ctxtid,
KGSL_IOCTL_LOCK),
KGSL_IOCTL_FUNC(IOCTL_KGSL_RINGBUFFER_ISSUEIBCMDS,
kgsl_ioctl_rb_issueibcmds, 0),
KGSL_IOCTL_FUNC(IOCTL_KGSL_SUBMIT_COMMANDS,
kgsl_ioctl_submit_commands, 0),
KGSL_IOCTL_FUNC(IOCTL_KGSL_CMDSTREAM_READTIMESTAMP,
kgsl_ioctl_cmdstream_readtimestamp,
KGSL_IOCTL_LOCK),
KGSL_IOCTL_FUNC(IOCTL_KGSL_CMDSTREAM_READTIMESTAMP_CTXTID,
kgsl_ioctl_cmdstream_readtimestamp_ctxtid,
KGSL_IOCTL_LOCK),
KGSL_IOCTL_FUNC(IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP,
kgsl_ioctl_cmdstream_freememontimestamp,
KGSL_IOCTL_LOCK),
KGSL_IOCTL_FUNC(IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP_CTXTID,
kgsl_ioctl_cmdstream_freememontimestamp_ctxtid,
KGSL_IOCTL_LOCK),
KGSL_IOCTL_FUNC(IOCTL_KGSL_DRAWCTXT_CREATE,
kgsl_ioctl_drawctxt_create,
KGSL_IOCTL_LOCK),
KGSL_IOCTL_FUNC(IOCTL_KGSL_DRAWCTXT_DESTROY,
kgsl_ioctl_drawctxt_destroy,
KGSL_IOCTL_LOCK),
KGSL_IOCTL_FUNC(IOCTL_KGSL_MAP_USER_MEM,
kgsl_ioctl_map_user_mem, 0),
KGSL_IOCTL_FUNC(IOCTL_KGSL_SHAREDMEM_FROM_PMEM,
kgsl_ioctl_map_user_mem, 0),
KGSL_IOCTL_FUNC(IOCTL_KGSL_SHAREDMEM_FREE,
kgsl_ioctl_sharedmem_free, 0),
KGSL_IOCTL_FUNC(IOCTL_KGSL_SHAREDMEM_FLUSH_CACHE,
kgsl_ioctl_sharedmem_flush_cache, 0),
KGSL_IOCTL_FUNC(IOCTL_KGSL_GPUMEM_ALLOC,
kgsl_ioctl_gpumem_alloc, 0),
KGSL_IOCTL_FUNC(IOCTL_KGSL_CFF_SYNCMEM,
kgsl_ioctl_cff_syncmem, 0),
KGSL_IOCTL_FUNC(IOCTL_KGSL_CFF_USER_EVENT,
kgsl_ioctl_cff_user_event, 0),
KGSL_IOCTL_FUNC(IOCTL_KGSL_TIMESTAMP_EVENT,
kgsl_ioctl_timestamp_event,
KGSL_IOCTL_LOCK),
KGSL_IOCTL_FUNC(IOCTL_KGSL_SETPROPERTY,
kgsl_ioctl_device_setproperty,
KGSL_IOCTL_LOCK),
KGSL_IOCTL_FUNC(IOCTL_KGSL_GPUMEM_ALLOC_ID,
kgsl_ioctl_gpumem_alloc_id, 0),
KGSL_IOCTL_FUNC(IOCTL_KGSL_GPUMEM_FREE_ID,
kgsl_ioctl_gpumem_free_id, 0),
KGSL_IOCTL_FUNC(IOCTL_KGSL_GPUMEM_GET_INFO,
kgsl_ioctl_gpumem_get_info, 0),
KGSL_IOCTL_FUNC(IOCTL_KGSL_GPUMEM_SYNC_CACHE,
kgsl_ioctl_gpumem_sync_cache, 0),
KGSL_IOCTL_FUNC(IOCTL_KGSL_GPUMEM_SYNC_CACHE_BULK,
kgsl_ioctl_gpumem_sync_cache_bulk, 0),
};
1.首先,我们来看漏洞的形成原因。
$1
漏洞产生代码 和 利用方案主要都在 都在kgsl_ioctl_gpumem_alloc_id函数 里面
首先,我们先看看漏洞产生的函数.
result = kgsl_mem_entry_attach_process(entry, dev_priv); // 这个函数产生了漏洞
现在我们来看看这里面都有些什么内容。
static int
kgsl_mem_entry_attach_process(struct kgsl_mem_entry *entry,
struct kgsl_device_private *dev_priv)
{
... 这里添加到了idr中
id = idr_alloc(&process->mem_idr, entry, 1, 0, GFP_NOWAIT);
...
在调用 kgsl_mmu_map 之前 把entry释放掉 造成UAF?
ret = kgsl_mmu_map(pagetable, &entry->memdesc);
...
}
上面是我打印出来的几个和漏洞相关的重要函数
首先是第一个 漏洞参数主要在这个函数内。 首先来看看 idr_alloc
这个函数 主要是给管理对象,会给对象分配一个编号,然后我们可以再次通过这个编号找到要管理的对象。
重要的是这个编号是按照顺序排序的。第一个对方存放进去的时候 返回的 ID 将会是1 这个很重要。销毁对象的时候我们将会用到
我们再来看看销毁对象是怎么做的。
销毁对象的方式有很多,但是其中有一个 IOCTL_KGSL_GPUMEM_FREE_ID 他是根据idr 编号找到对象,然后进行销毁。
释放内存 造成漏洞形成
ioctl(fd,IOCTL_KGSL_GPUMEM_FREE_ID, &arg_free);
通过调用路径我们找到 最终会调用下面的这个函数来销毁对象 ,我们再来具体分析
static long kgsl_ioctl_gpumem_free_id(struct kgsl_device_private *dev_priv,
unsigned int cmd, void *data)
{
struct kgsl_gpumem_free_id *param = data;
struct kgsl_process_private *private = dev_priv->process_priv;
struct kgsl_mem_entry *entry = NULL;
entry = kgsl_sharedmem_find_id(private, param->id);
...
/*
* First kgsl_mem_entry_put is for the reference that we took in
* this function when calling kgsl_sharedmem_find_id, second one is
* to free the memory since this is a free ioctl
*/
kgsl_mem_entry_put(entry);
return 0;
}
entry = kgsl_sharedmem_find_id(private, param->id);注意这个函数
他是根据ID 来获取到对象的,而我们已经知道idr_alloc的编号是按顺序发放的
struct kgsl_gpumem_free_id *param = data; 中的data 使我们从用户空间传入的
那么就是说 param->id 我们可以直接赋值 1的话,他会返回我们kgsl_mem_entry_attach_process函数中
管理的entry对象 最后销毁entry对象
这个调用过程,单线程跑的话。是不会有问题的。
但是如果我们假设用双线程跑到某种特殊的情况下又会是怎么样呢?
现在我们来假设一下情况。
首先第一个线程
kgsl_mem_entry_attach_process(struct kgsl_mem_entry *entry,
struct kgsl_device_private *dev_priv)
{
...
id = idr_alloc(&process->mem_idr, entry, 1, 0, GFP_NOWAIT);
线程1:跑到这里,然后创建了ID,保存了对象。 ID ret 1
这时候 cpu切换到线程2,开始执行线程2的代码
...
}
第二个线程
static long kgsl_ioctl_gpumem_free_id(struct kgsl_device_private *dev_priv,
unsigned int cmd, void *data)
{
entry = kgsl_sharedmem_find_id(private, param->id);
kgsl_mem_entry_put(entry);
线程2:执行完这个函数。根据ID 找到了 entry 对象,最后然后释放了entry
}
然后cpu切换到线程1再继续往下执行的话。我们发现 线程1所用的 entry 对象已经被线程2释放掉了。
这样漏洞就被触发了.
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课