/** * Close a file device previously opened by VBoxDrvFreeBSDOpen * * @returns 0 on success. * @param pDev The device. * @param fFile The file descriptor flags. * @param DevType The device type (CHR. * @param pTd The calling thread. */ static int VBoxDrvFreeBSDClose(struct cdev *pDev, int fFile, int DevType, struct thread *pTd) { PSUPDRVSESSION pSession = (PSUPDRVSESSION)pDev->si_drv1; #if __FreeBSD_version < 800062 Log(("VBoxDrvFreeBSDClose: fFile=%#x iUnit=%d pSession=%p\n", fFile, minor2unit(minor(pDev)), pSession)); #else Log(("VBoxDrvFreeBSDClose: fFile=%#x iUnit=%d pSession=%p\n", fFile, minor(dev2udev(pDev)), pSession)); #endif /* * Close the session if it's still hanging on to the device... */ if (VALID_PTR(pSession)) { supdrvCloseSession(&g_VBoxDrvFreeBSDDevExt, pSession); if (!ASMAtomicCmpXchgPtr(&pDev->si_drv1, NULL, pSession)) OSDBGPRINT(("VBoxDrvFreeBSDClose: si_drv1=%p expected %p!\n", pDev->si_drv1, pSession)); ASMAtomicDecU32(&g_cUsers); /* Don't use destroy_dev here because it may sleep resulting in a hanging user process. */ destroy_dev_sched(pDev); } else OSDBGPRINT(("VBoxDrvFreeBSDClose: si_drv1=%p!\n", pSession)); return 0; }
/** * Device I/O Control entry point. * * @returns Darwin for slow IOCtls and VBox status code for the fast ones. * @param Dev The device number (major+minor). * @param iCmd The IOCtl command. * @param pData Pointer to the data (if any it's a SUPDRVIOCTLDATA (kernel copy)). * @param fFlags Flag saying we're a character device (like we didn't know already). * @param pProcess The process issuing this request. */ static int VBoxDrvDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess) { const bool fUnrestricted = minor(Dev) == 0; const RTPROCESS Process = proc_pid(pProcess); const unsigned iHash = SESSION_HASH(Process); PSUPDRVSESSION pSession; /* * Find the session. */ RTSpinlockAcquire(g_Spinlock); pSession = g_apSessionHashTab[iHash]; while (pSession && pSession->Process != Process && pSession->fUnrestricted == fUnrestricted && pSession->fOpened) pSession = pSession->pNextHash; RTSpinlockReleaseNoInts(g_Spinlock); if (!pSession) { OSDBGPRINT(("VBoxDrvDarwinIOCtl: WHAT?!? pSession == NULL! This must be a mistake... pid=%d iCmd=%#lx\n", (int)Process, iCmd)); return EINVAL; } /* * Deal with the two high-speed IOCtl that takes it's arguments from * the session and iCmd, and only returns a VBox status code. */ if ( ( iCmd == SUP_IOCTL_FAST_DO_RAW_RUN || iCmd == SUP_IOCTL_FAST_DO_HM_RUN || iCmd == SUP_IOCTL_FAST_DO_NOP) && fUnrestricted) return supdrvIOCtlFast(iCmd, *(uint32_t *)pData, &g_DevExt, pSession); return VBoxDrvDarwinIOCtlSlow(pSession, iCmd, pData, pProcess); }
DECLASM(int) VBoxDrvIOCtlFast(uint16_t sfn, uint8_t iFunction) { /* * Find the session. */ const RTPROCESS Process = RTProcSelf(); const unsigned iHash = SESSION_HASH(sfn); PSUPDRVSESSION pSession; RTSpinlockAcquire(g_Spinlock); pSession = g_apSessionHashTab[iHash]; if (pSession && pSession->Process != Process) { do pSession = pSession->pNextHash; while ( pSession && ( pSession->sfn != sfn || pSession->Process != Process)); if (RT_LIKELY(pSession)) supdrvSessionRetain(pSession); } RTSpinlockReleaseNoInts(g_Spinlock); if (RT_UNLIKELY(!pSession)) { OSDBGPRINT(("VBoxDrvIoctl: WHUT?!? pSession == NULL! This must be a mistake... pid=%d\n", (int)Process)); return VERR_INVALID_PARAMETER; } /* * Dispatch the fast IOCtl. */ supdrvIOCtlFast(iFunction, 0, &g_DevExt, pSession); supdrvSessionRelease(pSession); return 0; }
DECLASM(int) VBoxDrvClose(uint16_t sfn) { Log(("VBoxDrvClose: pid=%d sfn=%d\n", (int)RTProcSelf(), sfn)); /* * Remove from the hash table. */ PSUPDRVSESSION pSession; const RTPROCESS Process = RTProcSelf(); const unsigned iHash = SESSION_HASH(sfn); RTSpinlockAcquire(g_Spinlock); pSession = g_apSessionHashTab[iHash]; if (pSession) { if ( pSession->sfn == sfn && pSession->Process == Process) { g_apSessionHashTab[iHash] = pSession->pNextHash; pSession->pNextHash = NULL; } else { PSUPDRVSESSION pPrev = pSession; pSession = pSession->pNextHash; while (pSession) { if ( pSession->sfn == sfn && pSession->Process == Process) { pPrev->pNextHash = pSession->pNextHash; pSession->pNextHash = NULL; break; } /* next */ pPrev = pSession; pSession = pSession->pNextHash; } } } RTSpinlockReleaseNoInts(g_Spinlock); if (!pSession) { OSDBGPRINT(("VBoxDrvIoctl: WHUT?!? pSession == NULL! This must be a mistake... pid=%d sfn=%d\n", (int)Process, sfn)); return VERR_INVALID_PARAMETER; } /* * Close the session. */ supdrvSessionRelease(pSession); return 0; }
static int VBoxDrvFreeBSDOpen(struct cdev *pDev, int fOpen, struct thread *pTd, int iFd) #endif { PSUPDRVSESSION pSession; int rc; #if __FreeBSD_version < 800062 Log(("VBoxDrvFreeBSDOpen: fOpen=%#x iUnit=%d\n", fOpen, minor2unit(minor(pDev)))); #else Log(("VBoxDrvFreeBSDOpen: fOpen=%#x iUnit=%d\n", fOpen, minor(dev2udev(pDev)))); #endif /* * Let's be a bit picky about the flags... */ if (fOpen != (FREAD|FWRITE /*=O_RDWR*/)) { Log(("VBoxDrvFreeBSDOpen: fOpen=%#x expected %#x\n", fOpen, O_RDWR)); return EINVAL; } /* * Try grab it (we don't grab the giant, remember). */ if (!ASMAtomicCmpXchgPtr(&pDev->si_drv1, (void *)0x42, NULL)) return EBUSY; /* * Create a new session. */ rc = supdrvCreateSession(&g_VBoxDrvFreeBSDDevExt, true /* fUser */, &pSession); if (RT_SUCCESS(rc)) { /** @todo get (r)uid and (r)gid. pSession->Uid = stuff; pSession->Gid = stuff; */ if (ASMAtomicCmpXchgPtr(&pDev->si_drv1, pSession, (void *)0x42)) { ASMAtomicIncU32(&g_cUsers); return 0; } OSDBGPRINT(("VBoxDrvFreeBSDOpen: si_drv1=%p, expected 0x42!\n", pDev->si_drv1)); supdrvCloseSession(&g_VBoxDrvFreeBSDDevExt, pSession); } return RTErrConvertToErrno(rc); }
/** * DEVFS event handler. */ static void VBoxDrvFreeBSDClone(void *pvArg, struct ucred *pCred, char *pszName, int cchName, struct cdev **ppDev) { int iUnit; int rc; Log(("VBoxDrvFreeBSDClone: pszName=%s ppDev=%p\n", pszName, ppDev)); /* * One device node per user, si_drv1 points to the session. * /dev/vboxdrv<N> where N = {0...255}. */ if (!ppDev) return; if (dev_stdclone(pszName, NULL, "vboxdrv", &iUnit) != 1) return; if (iUnit >= 256 || iUnit < 0) { Log(("VBoxDrvFreeBSDClone: iUnit=%d >= 256 - rejected\n", iUnit)); return; } Log(("VBoxDrvFreeBSDClone: pszName=%s iUnit=%d\n", pszName, iUnit)); rc = clone_create(&g_pVBoxDrvFreeBSDClones, &g_VBoxDrvFreeBSDChrDevSW, &iUnit, ppDev, 0); Log(("VBoxDrvFreeBSDClone: clone_create -> %d; iUnit=%d\n", rc, iUnit)); if (rc) { #if __FreeBSD_version > 800061 *ppDev = make_dev(&g_VBoxDrvFreeBSDChrDevSW, iUnit, UID_ROOT, GID_WHEEL, VBOXDRV_PERM, "vboxdrv%d", iUnit); #else *ppDev = make_dev(&g_VBoxDrvFreeBSDChrDevSW, unit2minor(iUnit), UID_ROOT, GID_WHEEL, VBOXDRV_PERM, "vboxdrv%d", iUnit); #endif if (*ppDev) { dev_ref(*ppDev); (*ppDev)->si_flags |= SI_CHEAPCLONE; Log(("VBoxDrvFreeBSDClone: Created *ppDev=%p iUnit=%d si_drv1=%p si_drv2=%p\n", *ppDev, iUnit, (*ppDev)->si_drv1, (*ppDev)->si_drv2)); (*ppDev)->si_drv1 = (*ppDev)->si_drv2 = NULL; } else OSDBGPRINT(("VBoxDrvFreeBSDClone: make_dev iUnit=%d failed\n", iUnit)); } else Log(("VBoxDrvFreeBSDClone: Existing *ppDev=%p iUnit=%d si_drv1=%p si_drv2=%p\n", *ppDev, iUnit, (*ppDev)->si_drv1, (*ppDev)->si_drv2)); }
/** * Worker for VBoxDrvNtDeviceControl that takes the slow IOCtl functions. * * @returns NT status code. * * @param pDevObj Device object. * @param pSession The session. * @param pIrp Request packet. * @param pStack The stack location containing the DeviceControl parameters. */ static int VBoxDrvNtDeviceControlSlow(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PIRP pIrp, PIO_STACK_LOCATION pStack) { NTSTATUS rcNt; unsigned cbOut = 0; int rc = 0; Log2(("VBoxDrvNtDeviceControlSlow(%p,%p): ioctl=%#x pBuf=%p cbIn=%#x cbOut=%#x pSession=%p\n", pDevExt, pIrp, pStack->Parameters.DeviceIoControl.IoControlCode, pIrp->AssociatedIrp.SystemBuffer, pStack->Parameters.DeviceIoControl.InputBufferLength, pStack->Parameters.DeviceIoControl.OutputBufferLength, pSession)); #ifdef RT_ARCH_AMD64 /* Don't allow 32-bit processes to do any I/O controls. */ if (!IoIs32bitProcess(pIrp)) #endif { /* Verify that it's a buffered CTL. */ if ((pStack->Parameters.DeviceIoControl.IoControlCode & 0x3) == METHOD_BUFFERED) { /* Verify that the sizes in the request header are correct. */ PSUPREQHDR pHdr = (PSUPREQHDR)pIrp->AssociatedIrp.SystemBuffer; if ( pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) && pStack->Parameters.DeviceIoControl.InputBufferLength == pHdr->cbIn && pStack->Parameters.DeviceIoControl.OutputBufferLength == pHdr->cbOut) { /* * Do the job. */ rc = supdrvIOCtl(pStack->Parameters.DeviceIoControl.IoControlCode, pDevExt, pSession, pHdr); if (!rc) { rcNt = STATUS_SUCCESS; cbOut = pHdr->cbOut; if (cbOut > pStack->Parameters.DeviceIoControl.OutputBufferLength) { cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength; OSDBGPRINT(("VBoxDrvLinuxIOCtl: too much output! %#x > %#x; uCmd=%#x!\n", pHdr->cbOut, cbOut, pStack->Parameters.DeviceIoControl.IoControlCode)); } } else rcNt = STATUS_INVALID_PARAMETER; Log2(("VBoxDrvNtDeviceControlSlow: returns %#x cbOut=%d rc=%#x\n", rcNt, cbOut, rc)); } else { Log(("VBoxDrvNtDeviceControlSlow: Mismatching sizes (%#x) - Hdr=%#lx/%#lx Irp=%#lx/%#lx!\n", pStack->Parameters.DeviceIoControl.IoControlCode, pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbIn : 0, pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbOut : 0, pStack->Parameters.DeviceIoControl.InputBufferLength, pStack->Parameters.DeviceIoControl.OutputBufferLength)); rcNt = STATUS_INVALID_PARAMETER; } } else { Log(("VBoxDrvNtDeviceControlSlow: not buffered request (%#x) - not supported\n", pStack->Parameters.DeviceIoControl.IoControlCode)); rcNt = STATUS_NOT_SUPPORTED; } } #ifdef RT_ARCH_AMD64 else { Log(("VBoxDrvNtDeviceControlSlow: WOW64 req - not supported\n")); rcNt = STATUS_NOT_SUPPORTED; } #endif /* complete the request. */ pIrp->IoStatus.Status = rcNt; pIrp->IoStatus.Information = cbOut; IoCompleteRequest(pIrp, IO_NO_INCREMENT); return rcNt; }
/** * Device I/O Control entry point. * * @param pFilp Associated file pointer. * @param uCmd The function specified to ioctl(). * @param ulArg The argument specified to ioctl(). * @param pSession The session instance. */ static int VBoxDrvLinuxIOCtlSlow(struct file *pFilp, unsigned int uCmd, unsigned long ulArg, PSUPDRVSESSION pSession) { int rc; SUPREQHDR Hdr; PSUPREQHDR pHdr; uint32_t cbBuf; Log6(("VBoxDrvLinuxIOCtl: pFilp=%p uCmd=%#x ulArg=%p pid=%d/%d\n", pFilp, uCmd, (void *)ulArg, RTProcSelf(), current->pid)); /* * Read the header. */ if (RT_UNLIKELY(copy_from_user(&Hdr, (void *)ulArg, sizeof(Hdr)))) { Log(("VBoxDrvLinuxIOCtl: copy_from_user(,%#lx,) failed; uCmd=%#x\n", ulArg, uCmd)); return -EFAULT; } if (RT_UNLIKELY((Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC)) { Log(("VBoxDrvLinuxIOCtl: bad header magic %#x; uCmd=%#x\n", Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK, uCmd)); return -EINVAL; } /* * Buffer the request. */ cbBuf = RT_MAX(Hdr.cbIn, Hdr.cbOut); if (RT_UNLIKELY(cbBuf > _1M*16)) { Log(("VBoxDrvLinuxIOCtl: too big cbBuf=%#x; uCmd=%#x\n", cbBuf, uCmd)); return -E2BIG; } if (RT_UNLIKELY(_IOC_SIZE(uCmd) ? cbBuf != _IOC_SIZE(uCmd) : Hdr.cbIn < sizeof(Hdr))) { Log(("VBoxDrvLinuxIOCtl: bad ioctl cbBuf=%#x _IOC_SIZE=%#x; uCmd=%#x\n", cbBuf, _IOC_SIZE(uCmd), uCmd)); return -EINVAL; } pHdr = RTMemAlloc(cbBuf); if (RT_UNLIKELY(!pHdr)) { OSDBGPRINT(("VBoxDrvLinuxIOCtl: failed to allocate buffer of %d bytes for uCmd=%#x\n", cbBuf, uCmd)); return -ENOMEM; } if (RT_UNLIKELY(copy_from_user(pHdr, (void *)ulArg, Hdr.cbIn))) { Log(("VBoxDrvLinuxIOCtl: copy_from_user(,%#lx, %#x) failed; uCmd=%#x\n", ulArg, Hdr.cbIn, uCmd)); RTMemFree(pHdr); return -EFAULT; } if (Hdr.cbIn < cbBuf) RT_BZERO((uint8_t *)pHdr + Hdr.cbIn, cbBuf - Hdr.cbIn); /* * Process the IOCtl. */ stac(); rc = supdrvIOCtl(uCmd, &g_DevExt, pSession, pHdr, cbBuf); clac(); /* * Copy ioctl data and output buffer back to user space. */ if (RT_LIKELY(!rc)) { uint32_t cbOut = pHdr->cbOut; if (RT_UNLIKELY(cbOut > cbBuf)) { OSDBGPRINT(("VBoxDrvLinuxIOCtl: too much output! %#x > %#x; uCmd=%#x!\n", cbOut, cbBuf, uCmd)); cbOut = cbBuf; } if (RT_UNLIKELY(copy_to_user((void *)ulArg, pHdr, cbOut))) { /* this is really bad! */ OSDBGPRINT(("VBoxDrvLinuxIOCtl: copy_to_user(%#lx,,%#x); uCmd=%#x!\n", ulArg, cbOut, uCmd)); rc = -EFAULT; } } else { Log(("VBoxDrvLinuxIOCtl: pFilp=%p uCmd=%#x ulArg=%p failed, rc=%d\n", pFilp, uCmd, (void *)ulArg, rc)); rc = -EINVAL; } RTMemFree(pHdr); Log6(("VBoxDrvLinuxIOCtl: returns %d (pid=%d/%d)\n", rc, RTProcSelf(), current->pid)); return rc; }
/** * Deal with the 'slow' I/O control requests. * * @returns 0 on success, appropriate errno on failure. * @param pSession The session. * @param ulCmd The command. * @param pvData The request data. * @param pTd The calling thread. */ static int VBoxDrvFreeBSDIOCtlSlow(PSUPDRVSESSION pSession, u_long ulCmd, caddr_t pvData, struct thread *pTd) { PSUPREQHDR pHdr; uint32_t cbReq = IOCPARM_LEN(ulCmd); void *pvUser = NULL; /* * Buffered request? */ if ((IOC_DIRMASK & ulCmd) == IOC_INOUT) { pHdr = (PSUPREQHDR)pvData; if (RT_UNLIKELY(cbReq < sizeof(*pHdr))) { OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: cbReq=%#x < %#x; ulCmd=%#lx\n", cbReq, (int)sizeof(*pHdr), ulCmd)); return EINVAL; } if (RT_UNLIKELY((pHdr->fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC)) { OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: bad magic fFlags=%#x; ulCmd=%#lx\n", pHdr->fFlags, ulCmd)); return EINVAL; } if (RT_UNLIKELY( RT_MAX(pHdr->cbIn, pHdr->cbOut) != cbReq || pHdr->cbIn < sizeof(*pHdr) || pHdr->cbOut < sizeof(*pHdr))) { OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: max(%#x,%#x) != %#x; ulCmd=%#lx\n", pHdr->cbIn, pHdr->cbOut, cbReq, ulCmd)); return EINVAL; } } /* * Big unbuffered request? */ else if ((IOC_DIRMASK & ulCmd) == IOC_VOID && !cbReq) { /* * Read the header, validate it and figure out how much that needs to be buffered. */ SUPREQHDR Hdr; pvUser = *(void **)pvData; int rc = copyin(pvUser, &Hdr, sizeof(Hdr)); if (RT_UNLIKELY(rc)) { OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: copyin(%p,Hdr,) -> %#x; ulCmd=%#lx\n", pvUser, rc, ulCmd)); return rc; } if (RT_UNLIKELY((Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC)) { OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: bad magic fFlags=%#x; ulCmd=%#lx\n", Hdr.fFlags, ulCmd)); return EINVAL; } cbReq = RT_MAX(Hdr.cbIn, Hdr.cbOut); if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr) || Hdr.cbOut < sizeof(Hdr) || cbReq > _1M*16)) { OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: max(%#x,%#x); ulCmd=%#lx\n", Hdr.cbIn, Hdr.cbOut, ulCmd)); return EINVAL; } /* * Allocate buffer and copy in the data. */ pHdr = (PSUPREQHDR)RTMemTmpAlloc(cbReq); if (RT_UNLIKELY(!pHdr)) { OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: failed to allocate buffer of %d bytes; ulCmd=%#lx\n", cbReq, ulCmd)); return ENOMEM; } rc = copyin(pvUser, pHdr, Hdr.cbIn); if (RT_UNLIKELY(rc)) { OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: copyin(%p,%p,%#x) -> %#x; ulCmd=%#lx\n", pvUser, pHdr, Hdr.cbIn, rc, ulCmd)); RTMemTmpFree(pHdr); return rc; } } else { Log(("VBoxDrvFreeBSDIOCtlSlow: huh? cbReq=%#x ulCmd=%#lx\n", cbReq, ulCmd)); return EINVAL; } /* * Process the IOCtl. */ int rc = supdrvIOCtl(ulCmd, &g_VBoxDrvFreeBSDDevExt, pSession, pHdr); if (RT_LIKELY(!rc)) { /* * If unbuffered, copy back the result before returning. */ if (pvUser) { uint32_t cbOut = pHdr->cbOut; if (cbOut > cbReq) { OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: too much output! %#x > %#x; uCmd=%#lx!\n", cbOut, cbReq, ulCmd)); cbOut = cbReq; } rc = copyout(pHdr, pvUser, cbOut); if (RT_UNLIKELY(rc)) OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: copyout(%p,%p,%#x) -> %d; uCmd=%#lx!\n", pHdr, pvUser, cbOut, rc, ulCmd)); Log(("VBoxDrvFreeBSDIOCtlSlow: returns %d / %d ulCmd=%lx\n", 0, pHdr->rc, ulCmd)); /* cleanup */ RTMemTmpFree(pHdr); } } else { /* * The request failed, just clean up. */ if (pvUser) RTMemTmpFree(pHdr); Log(("VBoxDrvFreeBSDIOCtlSlow: ulCmd=%lx pData=%p failed, rc=%d\n", ulCmd, pvData, rc)); rc = EINVAL; } return rc; }
/** * Worker for VBoxDrvDarwinIOCtl that takes the slow IOCtl functions. * * @returns Darwin errno. * * @param pSession The session. * @param iCmd The IOCtl command. * @param pData Pointer to the kernel copy of the SUPDRVIOCTLDATA buffer. * @param pProcess The calling process. */ static int VBoxDrvDarwinIOCtlSlow(PSUPDRVSESSION pSession, u_long iCmd, caddr_t pData, struct proc *pProcess) { LogFlow(("VBoxDrvDarwinIOCtlSlow: pSession=%p iCmd=%p pData=%p pProcess=%p\n", pSession, iCmd, pData, pProcess)); /* * Buffered or unbuffered? */ PSUPREQHDR pHdr; user_addr_t pUser = 0; void *pvPageBuf = NULL; uint32_t cbReq = IOCPARM_LEN(iCmd); if ((IOC_DIRMASK & iCmd) == IOC_INOUT) { pHdr = (PSUPREQHDR)pData; if (RT_UNLIKELY(cbReq < sizeof(*pHdr))) { OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: cbReq=%#x < %#x; iCmd=%#lx\n", cbReq, (int)sizeof(*pHdr), iCmd)); return EINVAL; } if (RT_UNLIKELY((pHdr->fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC)) { OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: bad magic fFlags=%#x; iCmd=%#lx\n", pHdr->fFlags, iCmd)); return EINVAL; } if (RT_UNLIKELY( RT_MAX(pHdr->cbIn, pHdr->cbOut) != cbReq || pHdr->cbIn < sizeof(*pHdr) || pHdr->cbOut < sizeof(*pHdr))) { OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: max(%#x,%#x) != %#x; iCmd=%#lx\n", pHdr->cbIn, pHdr->cbOut, cbReq, iCmd)); return EINVAL; } } else if ((IOC_DIRMASK & iCmd) == IOC_VOID && !cbReq) { /* * Get the header and figure out how much we're gonna have to read. */ SUPREQHDR Hdr; pUser = (user_addr_t)*(void **)pData; int rc = copyin(pUser, &Hdr, sizeof(Hdr)); if (RT_UNLIKELY(rc)) { OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: copyin(%llx,Hdr,) -> %#x; iCmd=%#lx\n", (unsigned long long)pUser, rc, iCmd)); return rc; } if (RT_UNLIKELY((Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC)) { OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: bad magic fFlags=%#x; iCmd=%#lx\n", Hdr.fFlags, iCmd)); return EINVAL; } cbReq = RT_MAX(Hdr.cbIn, Hdr.cbOut); if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr) || Hdr.cbOut < sizeof(Hdr) || cbReq > _1M*16)) { OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: max(%#x,%#x); iCmd=%#lx\n", Hdr.cbIn, Hdr.cbOut, iCmd)); return EINVAL; } /* * Allocate buffer and copy in the data. */ pHdr = (PSUPREQHDR)RTMemTmpAlloc(cbReq); if (!pHdr) pvPageBuf = pHdr = (PSUPREQHDR)IOMallocAligned(RT_ALIGN_Z(cbReq, PAGE_SIZE), 8); if (RT_UNLIKELY(!pHdr)) { OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: failed to allocate buffer of %d bytes; iCmd=%#lx\n", cbReq, iCmd)); return ENOMEM; } rc = copyin(pUser, pHdr, Hdr.cbIn); if (RT_UNLIKELY(rc)) { OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: copyin(%llx,%p,%#x) -> %#x; iCmd=%#lx\n", (unsigned long long)pUser, pHdr, Hdr.cbIn, rc, iCmd)); if (pvPageBuf) IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE)); else RTMemTmpFree(pHdr); return rc; } } else { Log(("VBoxDrvDarwinIOCtlSlow: huh? cbReq=%#x iCmd=%#lx\n", cbReq, iCmd)); return EINVAL; } /* * Process the IOCtl. */ int rc = supdrvIOCtl(iCmd, &g_DevExt, pSession, pHdr); if (RT_LIKELY(!rc)) { /* * If not buffered, copy back the buffer before returning. */ if (pUser) { uint32_t cbOut = pHdr->cbOut; if (cbOut > cbReq) { OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: too much output! %#x > %#x; uCmd=%#lx!\n", cbOut, cbReq, iCmd)); cbOut = cbReq; } rc = copyout(pHdr, pUser, cbOut); if (RT_UNLIKELY(rc)) OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: copyout(%p,%llx,%#x) -> %d; uCmd=%#lx!\n", pHdr, (unsigned long long)pUser, cbOut, rc, iCmd)); /* cleanup */ if (pvPageBuf) IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE)); else RTMemTmpFree(pHdr); } } else { /* * The request failed, just clean up. */ if (pUser) { if (pvPageBuf) IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE)); else RTMemTmpFree(pHdr); } Log(("VBoxDrvDarwinIOCtlSlow: pid=%d iCmd=%lx pData=%p failed, rc=%d\n", proc_pid(pProcess), iCmd, (void *)pData, rc)); rc = EINVAL; } Log2(("VBoxDrvDarwinIOCtlSlow: returns %d\n", rc)); return rc; }
/** * Device open. Called on open /dev/vboxdrv * * @param Dev The device number. * @param fFlags ???. * @param fDevType ???. * @param pProcess The process issuing this request. */ static int VBoxDrvDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess) { #ifdef DEBUG_DARWIN_GIP char szName[128]; szName[0] = '\0'; proc_name(proc_pid(pProcess), szName, sizeof(szName)); Log(("VBoxDrvDarwinOpen: pid=%d '%s'\n", proc_pid(pProcess), szName)); #endif /* * Only two minor devices numbers are allowed. */ if (minor(Dev) != 0 && minor(Dev) != 1) return EACCES; /* * Find the session created by org_virtualbox_SupDrvClient, fail * if no such session, and mark it as opened. We set the uid & gid * here too, since that is more straight forward at this point. */ const bool fUnrestricted = minor(Dev) == 0; int rc = VINF_SUCCESS; PSUPDRVSESSION pSession = NULL; kauth_cred_t pCred = kauth_cred_proc_ref(pProcess); if (pCred) { #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 RTUID Uid = kauth_cred_getruid(pCred); RTGID Gid = kauth_cred_getrgid(pCred); #else RTUID Uid = pCred->cr_ruid; RTGID Gid = pCred->cr_rgid; #endif RTPROCESS Process = RTProcSelf(); unsigned iHash = SESSION_HASH(Process); RTSpinlockAcquire(g_Spinlock); pSession = g_apSessionHashTab[iHash]; while (pSession && pSession->Process != Process) pSession = pSession->pNextHash; if (pSession) { if (!pSession->fOpened) { pSession->fOpened = true; pSession->fUnrestricted = fUnrestricted; pSession->Uid = Uid; pSession->Gid = Gid; } else rc = VERR_ALREADY_LOADED; } else rc = VERR_GENERAL_FAILURE; RTSpinlockReleaseNoInts(g_Spinlock); #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 kauth_cred_unref(&pCred); #else /* 10.4 */ /* The 10.4u SDK headers and 10.4.11 kernel source have inconsistent definitions of kauth_cred_unref(), so use the other (now deprecated) API for releasing it. */ kauth_cred_rele(pCred); #endif /* 10.4 */ } else rc = VERR_INVALID_PARAMETER; #ifdef DEBUG_DARWIN_GIP OSDBGPRINT(("VBoxDrvDarwinOpen: pid=%d '%s' pSession=%p rc=%d\n", proc_pid(pProcess), szName, pSession, rc)); #else Log(("VBoxDrvDarwinOpen: g_DevExt=%p pSession=%p rc=%d pid=%d\n", &g_DevExt, pSession, rc, proc_pid(pProcess))); #endif return VBoxDrvDarwinErr2DarwinErr(rc); }
DECLASM(int) VBoxDrvIOCtl(uint16_t sfn, uint8_t iCat, uint8_t iFunction, void *pvParm, void *pvData, uint16_t *pcbParm, uint16_t *pcbData) { /* * Find the session. */ const RTPROCESS Process = RTProcSelf(); const unsigned iHash = SESSION_HASH(sfn); PSUPDRVSESSION pSession; RTSpinlockAcquire(g_Spinlock); pSession = g_apSessionHashTab[iHash]; if (pSession && pSession->Process != Process) { do pSession = pSession->pNextHash; while ( pSession && ( pSession->sfn != sfn || pSession->Process != Process)); if (RT_LIKELY(pSession)) supdrvSessionRetain(pSession); } RTSpinlockReleaseNoInts(g_Spinlock); if (!pSession) { OSDBGPRINT(("VBoxDrvIoctl: WHUT?!? pSession == NULL! This must be a mistake... pid=%d\n", (int)Process)); return VERR_INVALID_PARAMETER; } /* * Verify the category and dispatch the IOCtl. */ if (RT_LIKELY(iCat == SUP_CTL_CATEGORY)) { Log(("VBoxDrvIOCtl: pSession=%p iFunction=%#x pvParm=%p pvData=%p *pcbParm=%d *pcbData=%d\n", pSession, iFunction, pvParm, pvData, *pcbParm, *pcbData)); Assert(pvParm); Assert(!pvData); /* * Lock the header. */ PSUPREQHDR pHdr = (PSUPREQHDR)pvParm; AssertReturn(*pcbParm == sizeof(*pHdr), VERR_INVALID_PARAMETER); KernVMLock_t Lock; int rc = KernVMLock(VMDHL_WRITE, pHdr, *pcbParm, &Lock, (KernPageList_t *)-1, NULL); AssertMsgReturn(!rc, ("KernVMLock(VMDHL_WRITE, %p, %#x, &p, NULL, NULL) -> %d\n", pHdr, *pcbParm, &Lock, rc), VERR_LOCK_FAILED); /* * Validate the header. */ if (RT_LIKELY((pHdr->fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) == SUPREQHDR_FLAGS_MAGIC)) { uint32_t cbReq = RT_MAX(pHdr->cbIn, pHdr->cbOut); if (RT_LIKELY( pHdr->cbIn >= sizeof(*pHdr) && pHdr->cbOut >= sizeof(*pHdr) && cbReq <= _1M*16)) { /* * Lock the rest of the buffer if necessary. */ if (((uintptr_t)pHdr & PAGE_OFFSET_MASK) + cbReq > PAGE_SIZE) { rc = KernVMUnlock(&Lock); AssertMsgReturn(!rc, ("KernVMUnlock(Lock) -> %#x\n", rc), VERR_LOCK_FAILED); rc = KernVMLock(VMDHL_WRITE, pHdr, cbReq, &Lock, (KernPageList_t *)-1, NULL); AssertMsgReturn(!rc, ("KernVMLock(VMDHL_WRITE, %p, %#x, &p, NULL, NULL) -> %d\n", pHdr, cbReq, &Lock, rc), VERR_LOCK_FAILED); } /* * Process the IOCtl. */ rc = supdrvIOCtl(iFunction, &g_DevExt, pSession, pHdr); } else { OSDBGPRINT(("VBoxDrvIOCtl: max(%#x,%#x); iCmd=%#x\n", pHdr->cbIn, pHdr->cbOut, iFunction)); rc = VERR_INVALID_PARAMETER; } } else { OSDBGPRINT(("VBoxDrvIOCtl: bad magic fFlags=%#x; iCmd=%#x\n", pHdr->fFlags, iFunction)); rc = VERR_INVALID_PARAMETER; } /* * Unlock and return. */ int rc2 = KernVMUnlock(&Lock); AssertMsg(!rc2, ("rc2=%d\n", rc2)); NOREF(rc2);s }