/** * Service request callback function. * * @returns VBox status code. * @param pSession The caller's session. * @param u64Arg 64-bit integer argument. * @param pReqHdr The request header. Input / Output. Optional. */ DECLEXPORT(int) TSTRTR0MemUserKernelSrvReqHandler(PSUPDRVSESSION pSession, uint32_t uOperation, uint64_t u64Arg, PSUPR0SERVICEREQHDR pReqHdr) { NOREF(pSession); if (!VALID_PTR(pReqHdr)) return VERR_INVALID_PARAMETER; char *pszErr = (char *)(pReqHdr + 1); size_t cchErr = pReqHdr->cbReq - sizeof(*pReqHdr); if (cchErr < 32 || cchErr >= 0x10000) return VERR_INVALID_PARAMETER; *pszErr = '\0'; /* * R3Ptr is valid and good for up to a page. The page before * and after are both invalid. Or, it's a kernel page. */ RTR3PTR R3Ptr = (RTR3PTR)u64Arg; if (R3Ptr != u64Arg) return VERR_INVALID_PARAMETER; /* * Allocate a kernel buffer. */ uint8_t *pbKrnlBuf = (uint8_t *)RTMemAlloc(PAGE_SIZE * 2); if (!pbKrnlBuf) { RTStrPrintf(pszErr, cchErr, "!no memory for kernel buffers"); return VINF_SUCCESS; } /* * The big switch. */ switch (uOperation) { case TSTRTR0MEMUSERKERNEL_SANITY_OK: break; case TSTRTR0MEMUSERKERNEL_SANITY_FAILURE: RTStrPrintf(pszErr, cchErr, "!42failure42%1024s", ""); break; case TSTRTR0MEMUSERKERNEL_BASIC: { int rc = RTR0MemUserCopyFrom(pbKrnlBuf, R3Ptr, PAGE_SIZE); if (rc == VINF_SUCCESS) { rc = RTR0MemUserCopyTo(R3Ptr, pbKrnlBuf, PAGE_SIZE); if (rc == VINF_SUCCESS) { if (RTR0MemUserIsValidAddr(R3Ptr)) { if (RTR0MemKernelIsValidAddr(pbKrnlBuf)) { if (RTR0MemAreKrnlAndUsrDifferent()) { RTStrPrintf(pszErr, cchErr, "RTR0MemAreKrnlAndUsrDifferent returns true"); if (!RTR0MemUserIsValidAddr((uintptr_t)pbKrnlBuf)) { if (!RTR0MemKernelIsValidAddr((void *)R3Ptr)) { /* done */ } else RTStrPrintf(pszErr, cchErr, "! #5 - RTR0MemKernelIsValidAddr -> true, expected false"); } else RTStrPrintf(pszErr, cchErr, "! #5 - RTR0MemUserIsValidAddr -> true, expected false"); } else RTStrPrintf(pszErr, cchErr, "RTR0MemAreKrnlAndUsrDifferent returns false"); } else RTStrPrintf(pszErr, cchErr, "! #4 - RTR0MemKernelIsValidAddr -> false, expected true"); } else RTStrPrintf(pszErr, cchErr, "! #3 - RTR0MemUserIsValidAddr -> false, expected true"); } else RTStrPrintf(pszErr, cchErr, "! #2 - RTR0MemUserCopyTo -> %Rrc expected %Rrc", rc, VINF_SUCCESS); } else RTStrPrintf(pszErr, cchErr, "! #1 - RTR0MemUserCopyFrom -> %Rrc expected %Rrc", rc, VINF_SUCCESS); break; } #define TEST_OFF_SIZE(off, size, rcExpect) \ if (1) \ { \ int rc = RTR0MemUserCopyFrom(pbKrnlBuf, R3Ptr + (off), (size)); \ if (rc != (rcExpect)) \ { \ RTStrPrintf(pszErr, cchErr, "!RTR0MemUserCopyFrom(, +%#x, %#x) -> %Rrc, expected %Rrc", \ (off), (size), rc, (rcExpect)); \ break; \ } \ rc = RTR0MemUserCopyTo(R3Ptr + (off), pbKrnlBuf, (size)); \ if (rc != (rcExpect)) \ { \ RTStrPrintf(pszErr, cchErr, "!RTR0MemUserCopyTo(+%#x,, %#x) -> %Rrc, expected %Rrc", \ (off), (size), rc, (rcExpect)); \ break; \ } \ } else do {} while (0) case TSTRTR0MEMUSERKERNEL_GOOD: { for (unsigned off = 0; off < 16 && !*pszErr; off++) for (unsigned cb = 0; cb < PAGE_SIZE - 16; cb++) TEST_OFF_SIZE(off, cb, VINF_SUCCESS); break; } case TSTRTR0MEMUSERKERNEL_BAD: { for (unsigned off = 0; off < 16 && !*pszErr; off++) for (unsigned cb = 0; cb < PAGE_SIZE - 16; cb++) TEST_OFF_SIZE(off, cb, cb > 0 ? VERR_ACCESS_DENIED : VINF_SUCCESS); break; } case TSTRTR0MEMUSERKERNEL_INVALID_ADDRESS: { if ( !RTR0MemUserIsValidAddr(R3Ptr) && RTR0MemKernelIsValidAddr((void *)R3Ptr)) { for (unsigned off = 0; off < 16 && !*pszErr; off++) for (unsigned cb = 0; cb < PAGE_SIZE - 16; cb++) TEST_OFF_SIZE(off, cb, cb > 0 ? VERR_ACCESS_DENIED : VINF_SUCCESS); /* ... */ } else RTStrPrintf(pszErr, cchErr, "RTR0MemUserIsValidAddr returns true"); break; } default: RTStrPrintf(pszErr, cchErr, "!Unknown test #%d", uOperation); break; } /* The error indicator is the '!' in the message buffer. */ RTMemFree(pbKrnlBuf); return VINF_SUCCESS; }
/** * 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_FAILURE(RTR0MemUserCopyFrom(&Hdr, 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_FAILURE(RTR0MemUserCopyFrom(pHdr, 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. */ rc = supdrvIOCtl(uCmd, &g_DevExt, pSession, pHdr, cbBuf); /* * 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_FAILURE(RTR0MemUserCopyTo(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; }