/** * Calculates the decoded string length. * * @returns Number of chars (excluding the terminator). * @param pszString The string to decode. * @param cchMax The maximum string length (e.g. RTSTR_MAX). */ static size_t rtUriCalcDecodedLength(const char *pszString, size_t cchMax) { size_t cchDecoded; if (pszString) { size_t cchSrcLeft = cchDecoded = RTStrNLen(pszString, cchMax); while (cchSrcLeft-- > 0) { char const ch = *pszString++; if (ch != '%') { /* typical */} else if ( cchSrcLeft >= 2 && RT_C_IS_XDIGIT(pszString[0]) && RT_C_IS_XDIGIT(pszString[1])) { cchDecoded -= 2; pszString += 2; cchSrcLeft -= 2; } } } else cchDecoded = 0; return cchDecoded; }
/** * Encodes an URI into a caller allocated buffer. * * @returns IPRT status code. * @param pszString The string to encode. * @param cchMax The maximum string length (e.g. RTSTR_MAX). * @param fEncodeDosSlash Whether to encode DOS slashes or not. * @param pszDst The destination buffer. * @param cbDst The size of the destination buffer. */ static int rtUriEncodeIntoBuffer(const char *pszString, size_t cchMax, bool fEncodeDosSlash, char *pszDst, size_t cbDst) { AssertReturn(pszString, VERR_INVALID_POINTER); AssertPtrReturn(pszDst, VERR_INVALID_POINTER); /* * We do buffer size checking up front and every time we encode a special * character. That's faster than checking for each char. */ size_t cchSrcLeft = RTStrNLen(pszString, cchMax); AssertMsgReturn(cbDst > cchSrcLeft, ("cbDst=%zu cchSrcLeft=%zu\n", cbDst, cchSrcLeft), VERR_BUFFER_OVERFLOW); cbDst -= cchSrcLeft; while (cchSrcLeft-- > 0) { char const ch = *pszString++; if (!URI_EXCLUDED(ch) || (ch == '\\' && !fEncodeDosSlash)) *pszDst++ = ch; else { AssertReturn(cbDst >= 3, VERR_BUFFER_OVERFLOW); /* 2 extra bytes + zero terminator. */ cbDst -= 2; *pszDst++ = '%'; ssize_t cchTmp = RTStrFormatU8(pszDst, 3, (unsigned char)ch, 16, 2, 2, RTSTR_F_CAPITAL | RTSTR_F_ZEROPAD); Assert(cchTmp == 2); NOREF(cchTmp); pszDst += 2; } } *pszDst = '\0'; return VINF_SUCCESS; }
DECLINLINE(bool) tftpIsAcceptableOption(const char *pszOptionName) { int idxOptDesc = 0; AssertPtrReturn(pszOptionName, false); AssertReturn(RTStrNLen(pszOptionName,10) >= 4, false); AssertReturn(RTStrNLen(pszOptionName,10) < 8, false); for(idxOptDesc = 0; idxOptDesc < RT_ELEMENTS(g_TftpTransferFmtDesc); ++idxOptDesc) { if (!RTStrNICmp(pszOptionName, g_TftpTransferFmtDesc[idxOptDesc].pszName, 10)) return true; } for(idxOptDesc = 0; idxOptDesc < RT_ELEMENTS(g_TftpDesc); ++idxOptDesc) { if (!RTStrNICmp(pszOptionName, g_TftpDesc[idxOptDesc].pszName, 10)) return true; } return false; }
RTDECL(int) RTStrAAppendExNVTag(char **ppsz, size_t cPairs, va_list va, const char *pszTag) { AssertPtr(ppsz); if (!cPairs) return VINF_SUCCESS; /* * Determine the length of each string and calc the new total. */ struct RTStrAAppendExNVStruct { const char *psz; size_t cch; } *paPairs = (struct RTStrAAppendExNVStruct *)alloca(cPairs * sizeof(*paPairs)); AssertReturn(paPairs, VERR_NO_STR_MEMORY); size_t cchOrg = *ppsz ? strlen(*ppsz) : 0; size_t cchNewTotal = cchOrg; for (size_t i = 0; i < cPairs; i++) { const char *psz = va_arg(va, const char *); size_t cch = va_arg(va, size_t); AssertPtrNull(psz); Assert(cch == RTSTR_MAX || cch == RTStrNLen(psz, cch)); if (cch == RTSTR_MAX) cch = psz ? strlen(psz) : 0; cchNewTotal += cch; paPairs[i].cch = cch; paPairs[i].psz = psz; } cchNewTotal++; /* '\0' */ /* * Try reallocate the string. */ char *pszNew = (char *)RTMemReallocTag(*ppsz, cchNewTotal, pszTag); if (!pszNew) return VERR_NO_STR_MEMORY; /* * Do the appending. */ size_t off = cchOrg; for (size_t i = 0; i < cPairs; i++) { memcpy(&pszNew[off], paPairs[i].psz, paPairs[i].cch); off += paPairs[i].cch; } Assert(off + 1 == cchNewTotal); pszNew[off] = '\0'; /* done */ *ppsz = pszNew; return VINF_SUCCESS; }
DECLINLINE(void) tftpProcessRRQ(PNATState pData, PCTFTPIPHDR pTftpIpHeader, int pktlen) { PTFTPSESSION pTftpSession = NULL; uint8_t *pu8Payload = NULL; int cbPayload = 0; size_t cbFileName = 0; int rc = VINF_SUCCESS; AssertPtrReturnVoid(pTftpIpHeader); AssertPtrReturnVoid(pData); AssertReturnVoid(pktlen > sizeof(TFTPIPHDR)); LogFlowFunc(("ENTER: pTftpIpHeader:%p, pktlen:%d\n", pTftpIpHeader, pktlen)); rc = tftpAllocateSession(pData, pTftpIpHeader, &pTftpSession); if ( RT_FAILURE(rc) || pTftpSession == NULL) { LogFlowFuncLeave(); return; } pu8Payload = (uint8_t *)&pTftpIpHeader->Core; cbPayload = pktlen - sizeof(TFTPIPHDR); cbFileName = RTStrNLen((char *)pu8Payload, cbPayload); /* We assume that file name should finish with '\0' and shouldn't bigger * than buffer for name storage. */ AssertReturnVoid( cbFileName < cbPayload && cbFileName < TFTP_FILENAME_MAX /* current limit in tftp session handle */ && cbFileName); /* Dont't bother with rest processing in case of invalid access */ if (RT_FAILURE(tftpSecurityFilenameCheck(pData, pTftpSession))) { tftpSendError(pData, pTftpSession, 2, "Access violation", pTftpIpHeader); LogFlowFuncLeave(); return; } if (RT_UNLIKELY(!tftpIsSupportedTransferMode(pTftpSession))) { tftpSendError(pData, pTftpSession, 4, "Unsupported transfer mode", pTftpIpHeader); LogFlowFuncLeave(); return; } tftpSendOACK(pData, pTftpSession, pTftpIpHeader); LogFlowFuncLeave(); return; }
/** * Calculates the encoded string length. * * @returns Number of chars (excluding the terminator). * @param pszString The string to encode. * @param cchMax The maximum string length (e.g. RTSTR_MAX). * @param fEncodeDosSlash Whether to encode DOS slashes or not. */ static size_t rtUriCalcEncodedLength(const char *pszString, size_t cchMax, bool fEncodeDosSlash) { size_t cchEncoded = 0; if (pszString) { size_t cchSrcLeft = RTStrNLen(pszString, cchMax); while (cchSrcLeft-- > 0) { char const ch = *pszString++; if (!URI_EXCLUDED(ch) || (ch == '\\' && !fEncodeDosSlash)) cchEncoded += 1; else cchEncoded += 3; } } return cchEncoded; }
/** * This function evaluate file name. * @param pu8Payload * @param cbPayload * @param cbFileName * @return VINF_SUCCESS - * VERR_INVALID_PARAMETER - */ DECLINLINE(int) tftpSecurityFilenameCheck(PNATState pData, PCTFTPSESSION pcTftpSession) { size_t cbSessionFilename = 0; int rc = VINF_SUCCESS; AssertPtrReturn(pcTftpSession, VERR_INVALID_PARAMETER); cbSessionFilename = RTStrNLen((const char *)pcTftpSession->pszFilename, TFTP_FILENAME_MAX); if ( !RTStrNCmp((const char*)pcTftpSession->pszFilename, "../", 3) || (pcTftpSession->pszFilename[cbSessionFilename - 1] == '/') || RTStrStr((const char *)pcTftpSession->pszFilename, "/../")) rc = VERR_FILE_NOT_FOUND; /* only allow exported prefixes */ if ( RT_SUCCESS(rc) && !tftp_prefix) rc = VERR_INTERNAL_ERROR; LogFlowFuncLeaveRC(rc); return rc; }
RTDECL(int) RTPathJoinEx(char *pszPathDst, size_t cbPathDst, const char *pszPathSrc, size_t cchPathSrcMax, const char *pszAppend, size_t cchAppendMax) { AssertPtr(pszPathDst); AssertPtr(pszPathSrc); AssertPtr(pszAppend); /* * The easy way: Copy the path into the buffer and call RTPathAppend. */ size_t cchPathSrc = RTStrNLen(pszPathSrc, cchPathSrcMax); if (cchPathSrc >= cbPathDst) return VERR_BUFFER_OVERFLOW; memcpy(pszPathDst, pszPathSrc, cchPathSrc); pszPathDst[cchPathSrc] = '\0'; return RTPathAppendEx(pszPathDst, cbPathDst, pszAppend, cchAppendMax); }
RTDECL(int) RTStrAAppendNTag(char **ppsz, const char *pszAppend, size_t cchAppend, const char *pszTag) { size_t cchOrg; char *pszNew; if (!cchAppend) return VINF_SUCCESS; if (cchAppend == RTSTR_MAX) cchAppend = strlen(pszAppend); else Assert(cchAppend == RTStrNLen(pszAppend, cchAppend)); cchOrg = *ppsz ? strlen(*ppsz) : 0; pszNew = (char *)RTMemReallocTag(*ppsz, cchOrg + cchAppend + 1, pszTag); if (!pszNew) return VERR_NO_STR_MEMORY; memcpy(&pszNew[cchOrg], pszAppend, cchAppend); pszNew[cchOrg + cchAppend] = '\0'; *ppsz = pszNew; return VINF_SUCCESS; }
void Bstr::copyFromN(const char *a_pszSrc, size_t a_cchMax) { /* * Initialie m_bstr first in case of throws further down in the code, then * check for empty input (m_bstr == NULL means empty, there are no NULL * strings). */ m_bstr = NULL; if (!a_cchMax || !a_pszSrc || !*a_pszSrc) return; /* * Calculate the length and allocate a BSTR string buffer of the right * size, i.e. optimize heap usage. */ size_t cwc; int vrc = ::RTStrCalcUtf16LenEx(a_pszSrc, a_cchMax, &cwc); if (RT_SUCCESS(vrc)) { m_bstr = ::SysAllocStringByteLen(NULL, (unsigned)(cwc * sizeof(OLECHAR))); if (RT_LIKELY(m_bstr)) { PRTUTF16 pwsz = (PRTUTF16)m_bstr; vrc = ::RTStrToUtf16Ex(a_pszSrc, a_cchMax, &pwsz, cwc + 1, NULL); if (RT_SUCCESS(vrc)) return; /* This should not happen! */ AssertRC(vrc); cleanup(); } } else /* ASSUME: input is valid Utf-8. Fake out of memory error. */ AssertLogRelMsgFailed(("%Rrc %.*Rhxs\n", vrc, RTStrNLen(a_pszSrc, a_cchMax), a_pszSrc)); throw std::bad_alloc(); }
RTDECL(int) RTPathAppendEx(char *pszPath, size_t cbPathDst, const char *pszAppend, size_t cchAppendMax) { char *pszPathEnd = RTStrEnd(pszPath, cbPathDst); AssertReturn(pszPathEnd, VERR_INVALID_PARAMETER); /* * Special cases. */ if (!pszAppend) return VINF_SUCCESS; size_t cchAppend = RTStrNLen(pszAppend, cchAppendMax); if (!cchAppend) return VINF_SUCCESS; if (pszPathEnd == pszPath) { if (cchAppend >= cbPathDst) return VERR_BUFFER_OVERFLOW; memcpy(pszPath, pszAppend, cchAppend); pszPath[cchAppend] = '\0'; return VINF_SUCCESS; } /* * Balance slashes and check for buffer overflow. */ if (!RTPATH_IS_SLASH(pszPathEnd[-1])) { if (!RTPATH_IS_SLASH(pszAppend[0])) { #if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS) if ( (size_t)(pszPathEnd - pszPath) == 2 && pszPath[1] == ':' && RT_C_IS_ALPHA(pszPath[0])) { if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst) return VERR_BUFFER_OVERFLOW; } else #endif { if ((size_t)(pszPathEnd - pszPath) + 1 + cchAppend >= cbPathDst) return VERR_BUFFER_OVERFLOW; *pszPathEnd++ = RTPATH_SLASH; } } else { /* One slash is sufficient at this point. */ while (cchAppend > 1 && RTPATH_IS_SLASH(pszAppend[1])) pszAppend++, cchAppend--; if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst) return VERR_BUFFER_OVERFLOW; } } else { /* No slashes needed in the appended bit. */ while (cchAppend && RTPATH_IS_SLASH(*pszAppend)) pszAppend++, cchAppend--; /* In the leading path we can skip unnecessary trailing slashes, but be sure to leave one. */ size_t const cchRoot = rtPathRootSpecLen2(pszPath); while ( (size_t)(pszPathEnd - pszPath) > RT_MAX(1, cchRoot) && RTPATH_IS_SLASH(pszPathEnd[-2])) pszPathEnd--; if ((size_t)(pszPathEnd - pszPath) + cchAppend >= cbPathDst) return VERR_BUFFER_OVERFLOW; } /* * What remains now is the just the copying. */ memcpy(pszPathEnd, pszAppend, cchAppend); pszPathEnd[cchAppend] = '\0'; return VINF_SUCCESS; }
/** * Parses and validates a TAR header. * * @returns IPRT status code. * @param pThis The TAR reader stat. * @param pTar The TAR header that has been read. */ static int rtZipTarReaderParseHeader(PRTZIPTARREADER pThis, PCRTZIPTARHDR pHdr) { switch (pThis->enmState) { /* * The first record for a file/directory/whatever. */ case RTZIPTARREADERSTATE_FIRST: pThis->Hdr.Common.typeflag = 0x7f; pThis->enmPrevType = pThis->enmType; pThis->enmType = RTZIPTARTYPE_INVALID; pThis->offGnuLongCur = 0; pThis->cbGnuLongExpect = 0; pThis->szName[0] = '\0'; pThis->szTarget[0] = '\0'; return rtZipTarReaderParseNextHeader(pThis, pHdr, true /*fFirst*/); /* * There should only be so many zero headers at the end of the file as * it is a function of the block size used when writing. Don't go on * reading them forever in case someone points us to /dev/zero. */ case RTZIPTARREADERSTATE_ZERO: if (ASMMemIsAllU32(pHdr, sizeof(*pHdr), 0) != NULL) return VERR_TAR_ZERO_HEADER; pThis->cZeroHdrs++; if (pThis->cZeroHdrs <= _64K / 512 + 2) return VINF_SUCCESS; return VERR_TAR_ZERO_HEADER; case RTZIPTARREADERSTATE_GNU_LONGNAME: case RTZIPTARREADERSTATE_GNU_LONGLINK: { size_t cbIncoming = RTStrNLen((const char *)pHdr->ab, sizeof(*pHdr)); if (cbIncoming < sizeof(*pHdr)) cbIncoming += 1; if (cbIncoming + pThis->offGnuLongCur > pThis->cbGnuLongExpect) return VERR_TAR_MALFORMED_GNU_LONGXXXX; if ( cbIncoming < sizeof(*pHdr) && cbIncoming + pThis->offGnuLongCur != pThis->cbGnuLongExpect) return VERR_TAR_MALFORMED_GNU_LONGXXXX; char *pszDst = pThis->enmState == RTZIPTARREADERSTATE_GNU_LONGNAME ? pThis->szName : pThis->szTarget; pszDst += pThis->offGnuLongCur; memcpy(pszDst, pHdr->ab, cbIncoming); pThis->offGnuLongCur += (uint32_t)cbIncoming; if (pThis->offGnuLongCur == pThis->cbGnuLongExpect) pThis->enmState = RTZIPTARREADERSTATE_GNU_NEXT; return VINF_SUCCESS; } case RTZIPTARREADERSTATE_GNU_NEXT: pThis->enmState = RTZIPTARREADERSTATE_FIRST; return rtZipTarReaderParseNextHeader(pThis, pHdr, false /*fFirst*/); default: return VERR_INTERNAL_ERROR_5; } }
DECLINLINE(int) tftpSessionOptionParse(PTFTPSESSION pTftpSession, PCTFTPIPHDR pcTftpIpHeader) { int rc = VINF_SUCCESS; char *pszTftpRRQRaw; size_t idxTftpRRQRaw = 0; int cbTftpRRQRaw = 0; int fWithArg = 0; int idxOptionArg = 0; AssertPtrReturn(pTftpSession, VERR_INVALID_PARAMETER); AssertPtrReturn(pcTftpIpHeader, VERR_INVALID_PARAMETER); AssertReturn(RT_N2H_U16(pcTftpIpHeader->u16TftpOpType) == TFTP_RRQ, VERR_INVALID_PARAMETER); LogFlowFunc(("pTftpSession:%p, pcTftpIpHeader:%p\n", pTftpSession, pcTftpIpHeader)); pszTftpRRQRaw = (char *)&pcTftpIpHeader->Core; cbTftpRRQRaw = RT_H2N_U16(pcTftpIpHeader->UdpHdr.uh_ulen) + sizeof(struct ip) - RT_OFFSETOF(TFTPIPHDR, Core); while(cbTftpRRQRaw) { idxTftpRRQRaw = RTStrNLen(pszTftpRRQRaw, 512 - idxTftpRRQRaw) + 1; if (RTStrNLen((char *)pTftpSession->pszFilename, TFTP_FILENAME_MAX) == 0) { rc = RTStrCopy((char *)pTftpSession->pszFilename, TFTP_FILENAME_MAX, pszTftpRRQRaw); if (RT_FAILURE(rc)) { LogFlowFuncLeaveRC(rc); AssertRCReturn(rc,rc); } } else if (pTftpSession->enmTftpFmt == TFTPFMT_NONE) { int idxFmt = 0; rc = tftpFindTransferFormatIdxbyName(&idxFmt, pszTftpRRQRaw); if (RT_FAILURE(rc)) { LogFlowFuncLeaveRC(VERR_INTERNAL_ERROR); return VERR_INTERNAL_ERROR; } AssertReturn( g_TftpTransferFmtDesc[idxFmt].enmType != TFTPFMT_NONE && g_TftpTransferFmtDesc[idxFmt].enmType != TFTPFMT_NOT_FMT, VERR_INTERNAL_ERROR); pTftpSession->enmTftpFmt = g_TftpTransferFmtDesc[idxFmt].enmType; } else if (fWithArg) { if (!RTStrICmp("blksize", g_TftpDesc[idxOptionArg].pszName)) { rc = tftpSessionParseAndMarkOption(pszTftpRRQRaw, &pTftpSession->OptionBlkSize); if (pTftpSession->OptionBlkSize.u64Value > UINT16_MAX) rc = VERR_INVALID_PARAMETER; } if ( RT_SUCCESS(rc) && !RTStrICmp("tsize", g_TftpDesc[idxOptionArg].pszName)) rc = tftpSessionParseAndMarkOption(pszTftpRRQRaw, &pTftpSession->OptionTSize); /* @todo: we don't use timeout, but its value in the range 0-255 */ if ( RT_SUCCESS(rc) && !RTStrICmp("timeout", g_TftpDesc[idxOptionArg].pszName)) rc = tftpSessionParseAndMarkOption(pszTftpRRQRaw, &pTftpSession->OptionTimeout); /* @todo: unknown option detection */ if (RT_FAILURE(rc)) { LogFlowFuncLeaveRC(rc); AssertRCReturn(rc,rc); } fWithArg = 0; idxOptionArg = 0; } else { rc = tftpFindOptionIdxbyName(&idxOptionArg, pszTftpRRQRaw); if (RT_SUCCESS(rc)) fWithArg = 1; else { LogFlowFuncLeaveRC(rc); AssertRCReturn(rc,rc); } } pszTftpRRQRaw += idxTftpRRQRaw; cbTftpRRQRaw -= idxTftpRRQRaw; } LogFlowFuncLeaveRC(rc); return rc; }
/** * @interface_method_impl{DBGFOSIDMESG,pfnQueryKernelLog} */ static DECLCALLBACK(int) dbgDiggerLinuxIDmsg_QueryKernelLog(PDBGFOSIDMESG pThis, PUVM pUVM, uint32_t fFlags, uint32_t cMessages, char *pszBuf, size_t cbBuf, size_t *pcbActual) { PDBGDIGGERLINUX pData = RT_FROM_MEMBER(pThis, DBGDIGGERLINUX, IDmesg); if (cMessages < 1) return VERR_INVALID_PARAMETER; /* * Resolve the symbols we need and read their values. */ RTDBGAS hAs = DBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL); RTDBGMOD hMod; int rc = RTDbgAsModuleByName(hAs, "vmlinux", 0, &hMod); if (RT_FAILURE(rc)) return VERR_NOT_FOUND; RTDbgAsRelease(hAs); RTGCPTR GCPtrLogBuf; uint32_t cbLogBuf; uint32_t idxFirst; uint32_t idxNext; struct { void *pvVar; size_t cbHost, cbGuest; const char *pszSymbol; } aSymbols[] = { { &GCPtrLogBuf, sizeof(GCPtrLogBuf), pData->f64Bit ? sizeof(uint64_t) : sizeof(uint32_t), "log_buf" }, { &cbLogBuf, sizeof(cbLogBuf), sizeof(cbLogBuf), "log_buf_len" }, { &idxFirst, sizeof(idxFirst), sizeof(idxFirst), "log_first_idx" }, { &idxNext, sizeof(idxNext), sizeof(idxNext), "log_next_idx" }, }; for (uint32_t i = 0; i < RT_ELEMENTS(aSymbols); i++) { RTDBGSYMBOL SymInfo; rc = RTDbgModSymbolByName(hMod, aSymbols[i].pszSymbol, &SymInfo); if (RT_SUCCESS(rc)) { RT_BZERO(aSymbols[i].pvVar, aSymbols[i].cbHost); Assert(aSymbols[i].cbHost >= aSymbols[i].cbGuest); DBGFADDRESS Addr; rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, DBGFR3AddrFromFlat(pUVM, &Addr, (RTGCPTR)SymInfo.Value + pData->AddrKernelBase.FlatPtr), aSymbols[i].pvVar, aSymbols[i].cbGuest); if (RT_SUCCESS(rc)) continue; Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Reading '%s' at %RGv: %Rrc\n", aSymbols[i].pszSymbol, Addr.FlatPtr, rc)); } else Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Error looking up '%s': %Rrc\n", aSymbols[i].pszSymbol, rc)); RTDbgModRelease(hMod); return VERR_NOT_FOUND; } /* * Check if the values make sense. */ if (pData->f64Bit ? !LNX64_VALID_ADDRESS(GCPtrLogBuf) : !LNX32_VALID_ADDRESS(GCPtrLogBuf)) { Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_buf' value %RGv is not valid.\n", GCPtrLogBuf)); return VERR_NOT_FOUND; } if ( cbLogBuf < 4096 || !RT_IS_POWER_OF_TWO(cbLogBuf) || cbLogBuf > 16*_1M) { Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_buf_len' value %#x is not valid.\n", cbLogBuf)); return VERR_NOT_FOUND; } uint32_t const cbLogAlign = 4; if ( idxFirst > cbLogBuf - sizeof(LNXPRINTKHDR) || (idxFirst & (cbLogAlign - 1)) != 0) { Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_first_idx' value %#x is not valid.\n", idxFirst)); return VERR_NOT_FOUND; } if ( idxNext > cbLogBuf - sizeof(LNXPRINTKHDR) || (idxNext & (cbLogAlign - 1)) != 0) { Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_next_idx' value %#x is not valid.\n", idxNext)); return VERR_NOT_FOUND; } /* * Read the whole log buffer. */ uint8_t *pbLogBuf = (uint8_t *)RTMemAlloc(cbLogBuf); if (!pbLogBuf) { Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Failed to allocate %#x bytes for log buffer\n", cbLogBuf)); return VERR_NO_MEMORY; } DBGFADDRESS Addr; rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, DBGFR3AddrFromFlat(pUVM, &Addr, GCPtrLogBuf), pbLogBuf, cbLogBuf); if (RT_FAILURE(rc)) { Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Error reading %#x bytes of log buffer at %RGv: %Rrc\n", cbLogBuf, Addr.FlatPtr, rc)); RTMemFree(pbLogBuf); return VERR_NOT_FOUND; } /* * Count the messages in the buffer while doing some basic validation. */ uint32_t const cbUsed = idxFirst == idxNext ? cbLogBuf /* could be empty... */ : idxFirst < idxNext ? idxNext - idxFirst : cbLogBuf - idxFirst + idxNext; uint32_t cbLeft = cbUsed; uint32_t offCur = idxFirst; uint32_t cLogMsgs = 0; while (cbLeft > 0) { PCLNXPRINTKHDR pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur]; if (!pHdr->cbTotal) { /* Wrap around packet, most likely... */ if (cbLogBuf - offCur >= cbLeft) break; offCur = 0; pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur]; } if (RT_UNLIKELY( pHdr->cbTotal > cbLogBuf - sizeof(*pHdr) - offCur || pHdr->cbTotal > cbLeft || (pHdr->cbTotal & (cbLogAlign - 1)) != 0 || pHdr->cbTotal < (uint32_t)pHdr->cbText + (uint32_t)pHdr->cbDict + sizeof(*pHdr) )) { Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Invalid printk_log record at %#x: cbTotal=%#x cbText=%#x cbDict=%#x cbLogBuf=%#x cbLeft=%#x\n", offCur, pHdr->cbTotal, pHdr->cbText, pHdr->cbDict, cbLogBuf, cbLeft)); rc = VERR_INVALID_STATE; break; } if (pHdr->cbText > 0) cLogMsgs++; /* next */ offCur += pHdr->cbTotal; cbLeft -= pHdr->cbTotal; } if (RT_FAILURE(rc)) { RTMemFree(pbLogBuf); return rc; } /* * Copy the messages into the output buffer. */ offCur = idxFirst; cbLeft = cbUsed; /* Skip messages that the caller doesn't want. */ if (cMessages < cLogMsgs) { uint32_t cToSkip = cLogMsgs - cMessages; while (cToSkip > 0) { PCLNXPRINTKHDR pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur]; if (!pHdr->cbTotal) { offCur = 0; pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur]; } if (pHdr->cbText > 0) cToSkip--; /* next */ offCur += pHdr->cbTotal; cbLeft -= pHdr->cbTotal; } } /* Now copy the messages. */ size_t offDst = 0; while (cbLeft > 0) { PCLNXPRINTKHDR pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur]; if (!pHdr->cbTotal) { if (cbLogBuf - offCur >= cbLeft) break; offCur = 0; pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur]; } if (pHdr->cbText > 0) { char *pchText = (char *)(pHdr + 1); size_t cchText = RTStrNLen(pchText, pHdr->cbText); if (offDst + cchText < cbBuf) { memcpy(&pszBuf[offDst], pHdr + 1, cchText); pszBuf[offDst + cchText] = '\n'; } else if (offDst < cbBuf) memcpy(&pszBuf[offDst], pHdr + 1, cbBuf - offDst); offDst += cchText + 1; } /* next */ offCur += pHdr->cbTotal; cbLeft -= pHdr->cbTotal; } /* Done with the buffer. */ RTMemFree(pbLogBuf); /* Make sure we've reserved a char for the terminator. */ if (!offDst) offDst = 1; /* Set return size value. */ if (pcbActual) *pcbActual = offDst; /* * All VBox strings are UTF-8 and bad things may in theory happen if we * pass bad UTF-8 to code which assumes it's all valid. So, we enforce * UTF-8 upon the guest kernel messages here even if they (probably) have * no defined code set in reality. */ if (offDst <= cbBuf) { pszBuf[offDst - 1] = '\0'; RTStrPurgeEncoding(pszBuf); return VINF_SUCCESS; } if (cbBuf) { pszBuf[cbBuf - 1] = '\0'; RTStrPurgeEncoding(pszBuf); } return VERR_BUFFER_OVERFLOW; }
/** * Calculates the UTF-16 length of a Latin1 string. In fact this is just the * original length, but the function saves us nasty comments to that effect * all over the place. * * @returns IPRT status code. * @param psz Pointer to the Latin1 string. * @param cch The max length of the string. (btw cch = cb) * Use RTSTR_MAX if all of the string is to be examined.s * @param pcwc Where to store the length of the UTF-16 string as a number of RTUTF16 characters. */ static int rtLatin1CalcUtf16Length(const char *psz, size_t cch, size_t *pcwc) { *pcwc = RTStrNLen(psz, cch); return VINF_SUCCESS; }
/** * Decodes a string into a buffer. * * @returns IPRT status code. * @param pchSrc The source string. * @param cchSrc The max number of bytes to decode in the source string. * @param pszDst The destination buffer. * @param cbDst The size of the buffer (including terminator). */ static int rtUriDecodeIntoBuffer(const char *pchSrc, size_t cchSrc, char *pszDst, size_t cbDst) { AssertPtrReturn(pchSrc, VERR_INVALID_POINTER); AssertPtrReturn(pszDst, VERR_INVALID_POINTER); /* * Knowing that the pszString itself is valid UTF-8, we only have to * validate the escape sequences. */ cchSrc = RTStrNLen(pchSrc, cchSrc); while (cchSrc > 0) { const char *pchPct = (const char *)memchr(pchSrc, '%', cchSrc); if (pchPct) { size_t cchBefore = pchPct - pchSrc; AssertReturn(cchBefore + 1 < cbDst, VERR_BUFFER_OVERFLOW); if (cchBefore) { memcpy(pszDst, pchSrc, cchBefore); pszDst += cchBefore; cbDst -= cchBefore; pchSrc += cchBefore; cchSrc -= cchBefore; } char chHigh, chLow; if ( cchSrc >= 3 && RT_C_IS_XDIGIT(chHigh = pchSrc[1]) && RT_C_IS_XDIGIT(chLow = pchSrc[2])) { uint8_t b = RT_C_IS_DIGIT(chHigh) ? chHigh - '0' : (chHigh & ~0x20) - 'A' + 10; b <<= 4; b |= RT_C_IS_DIGIT(chLow) ? chLow - '0' : (chLow & ~0x20) - 'A' + 10; *pszDst++ = (char)b; pchSrc += 3; cchSrc -= 3; } else { AssertFailed(); *pszDst++ = *pchSrc++; cchSrc--; } cbDst -= 1; } else { AssertReturn(cchSrc < cbDst, VERR_BUFFER_OVERFLOW); memcpy(pszDst, pchSrc, cchSrc); pszDst += cchSrc; cbDst -= cchSrc; pchSrc += cchSrc; cchSrc = 0; break; } } AssertReturn(cbDst > 0, VERR_BUFFER_OVERFLOW); *pszDst = '\0'; return VINF_SUCCESS; }