void CboValidatorCore::ValidateBlob() { AssertBytesRemaining(sizeof(Value32) + sizeof(Value16)); const size_t blobStart = GetPosition(); const size_t blobSize = ReadValue32().mUInt; const size_t blobEnd = blobStart + blobSize; const size_t idIndex = ReadValue16().mUShort; AssertBytesRemaining(blobSize - (GetPosition() - blobStart)); if (idIndex == mResult.mListBlobIdIndex) { ValidateListBlob(); } else if (idIndex == mResult.mFunctionBlobIdIndex) { ValidateFunctionBlob(blobEnd); } else if (idIndex == mResult.mDataBlobIdIndex) { ValidateDataBlob(blobEnd); } else { SetPosition(blobEnd); } if (GetPosition() != blobEnd) { CboIsInvalid(); } }
void DisassemblerCore::Disassemble() { // Skip some header information that is already included in the validation result. Skip((2 * sizeof(Value32)) + (7 * sizeof(Value16))); mOutStream.Print("Version %" BOND_PRIu32 ".%" BOND_PRIu32 "\n", mValidationResult.mMajorVersion, mValidationResult.mMinorVersion); mOutStream.Print("Pointer size: %d bits\n", (mValidationResult.mPointerSize == POINTER_64BIT) ? 64 : 32); for (size_t i = 0; i < mValidationResult.mValue32Count; ++i) { mValue32Table[i] = ReadValue32(); } for (size_t i = 0; i < mValidationResult.mValue64Count; ++i) { mValue64Table[i] = ReadValue64(); } char *stringBytes = mStringBytes.data(); for (size_t i = 0; i < mValidationResult.mStringCount; ++i) { const size_t length = ReadValue16().mUShort; mStringTable[i] = StringView(stringBytes, length); mCboStream.Read(stringBytes, length); stringBytes += length; *stringBytes++ = '\0'; } QualifiedName *name = mQualifiedNameTable.data(); const char **element = mQualifiedNameElementTable.data(); for (size_t i = 0; i < mValidationResult.mQualifiedNameCount; ++i) { *name++ = QualifiedName(element); const size_t numElements = ReadValue16().mUShort; for (size_t j = 0; j < numElements; ++j) { const size_t elementIndex = ReadValue16().mUShort; *element++ = mStringTable[elementIndex].GetString(); } *element++ = nullptr; } DisassembleBlob(); }
size_t CboValidatorCore::ValidateQualifiedNameIndex() { AssertBytesRemaining(sizeof(Value16)); const size_t nameIndex = ReadValue16().mUShort; if (nameIndex >= mResult.mQualifiedNameCount) { CboIsInvalid(); } return nameIndex; }
void DisassemblerCore::DisassembleParamListSignature() { const size_t numElements = ReadValue16().mUShort; for (size_t i = 0; i < numElements; ++i) { // Skip the frame pointer offset. Skip(sizeof(Value32)); if (i > 0) { mOutStream.Print(", "); } DisassembleSizeAndType(); } }
void CboValidatorCore::ValidateParamListSignature() { AssertBytesRemaining(sizeof(Value16)); const size_t numParams = ReadValue16().mUShort; AssertBytesRemaining(numParams * 2 * sizeof(Value32)); ++mResult.mParamListSignatureCount; mResult.mParamSignatureCount += numParams; int32_t prevOffset = 0; for (size_t i = 0; i < numParams; ++i) { const int32_t offset = ReadValue32().mInt; if (offset >= prevOffset) { FunctionIsInvalid(); } prevOffset = offset; ValidateSizeAndType(); } }
void DisassemblerCore::DisassembleBlob() { const size_t blobStart = GetPosition(); const size_t blobSize = ReadValue32().mUInt; const size_t blobEnd = blobStart + blobSize; const size_t idIndex = ReadValue16().mUShort; if (idIndex == mValidationResult.mListBlobIdIndex) { DisassembleListBlob(); } else if (idIndex == mValidationResult.mFunctionBlobIdIndex) { DisassembleFunctionBlob(blobEnd); } else if (idIndex == mValidationResult.mDataBlobIdIndex) { DisassembleDataBlob(blobEnd); } else { SetPosition(blobEnd); } }
void DisassemblerCore::DisassembleDataBlob(size_t blobEnd) { const size_t nameIndex = ReadValue16().mUShort; mOutStream.Print("Data: "); const SignatureType type = DisassembleSizeAndType(); mOutStream.Print(" "); mQualifiedNameTable[nameIndex].PrintTo(mOutStream); const Value32 payload = ReadValue32(); switch (type) { case SIG_BOOL: { mOutStream.Print(" = %s\n", (payload.mInt == 0) ? "false" : "true"); } break; case SIG_CHAR: case SIG_UCHAR: { mOutStream.Print(" = %c\n", payload.mInt); } break; case SIG_SHORT: case SIG_INT: { mOutStream.Print(" = %" BOND_PRId32 "\n", payload.mInt); } break; case SIG_USHORT: case SIG_UINT: { mOutStream.Print(" = %" BOND_PRIu32 "\n", payload.mUInt); } break; case SIG_LONG: { const int64_t value = IsInRange<uint16_t>(payload.mUInt) ? mValue64Table[payload.mUInt].mLong : 0; mOutStream.Print(" = %" BOND_PRId64 "\n", value); } break; case SIG_ULONG: { const uint64_t value = IsInRange<uint16_t>(payload.mUInt) ? mValue64Table[payload.mUInt].mULong : 0; mOutStream.Print(" = %" BOND_PRIu64 "\n", value); } break; case SIG_FLOAT: { mOutStream.Print(" = %f\n", payload.mFloat); } break; case SIG_DOUBLE: { const double value = IsInRange<uint16_t>(payload.mUInt) ? mValue64Table[payload.mUInt].mDouble : 0.0; mOutStream.Print(" = %f\n", value); } break; case SIG_VOID: case SIG_POINTER: case SIG_AGGREGATE: { mOutStream.Print("\n\talignment: %" BOND_PRIu32 "\n", payload.mUInt); } break; } // Disassemble the optional metadata blob. if (GetPosition() < blobEnd) { DisassembleBlob(); } }
void DisassemblerCore::DisassembleFunctionBlob(size_t blobEnd) { const size_t functionNameIndex = ReadValue16().mUShort; mOutStream.Print("Function: "); DisassembleSizeAndType(); mOutStream.Print(" "); mQualifiedNameTable[functionNameIndex].PrintTo(mOutStream); mOutStream.Print("("); DisassembleParamListSignature(); mOutStream.Print(")\n"); const uint32_t argSize = ReadValue32().mUInt; const uint32_t packedArgSize = ReadValue32().mUInt; const uint32_t localSize = ReadValue32().mUInt; const uint32_t stackSize = ReadValue32().mUInt; const uint32_t framePointerAlignment = ReadValue32().mUInt; const uint32_t codeSize = ReadValue32().mUInt; const size_t codeStart = GetPosition(); const size_t codeEnd = codeStart + codeSize; mOutStream.Print( "\targ size: %" BOND_PRIu32 "\n" "\tpacked arg size: %" BOND_PRIu32 "\n" "\tlocal size: %" BOND_PRIu32 "\n" "\tstack size: %" BOND_PRIu32 "\n" "\tframe pointer alignment: %" BOND_PRIu32 "\n" "\tcode size: %" BOND_PRIu32 "\n", argSize, packedArgSize, localSize, stackSize, framePointerAlignment, codeSize); while (GetPosition() < codeEnd) { const uint32_t opCodeAddress = uint32_t(GetPosition() - codeStart); const OpCode opCode = static_cast<OpCode>(mCboStream.Read()); const OpCodeParam param = GetOpCodeParamType(opCode); mOutStream.Print("%6" BOND_PRIu32 ": %-12s", opCodeAddress, GetOpCodeMnemonic(opCode)); switch (param) { case OC_PARAM_NONE: break; case OC_PARAM_CHAR: mOutStream.Print("%" BOND_PRId32, int32_t(int8_t(mCboStream.Read()))); break; case OC_PARAM_UCHAR: mOutStream.Print("%" BOND_PRIu32, uint32_t(mCboStream.Read())); break; case OC_PARAM_UCHAR_CHAR: mOutStream.Print("%" BOND_PRIu32 ", %" BOND_PRId32, uint32_t(mCboStream.Read()), int32_t(int8_t(mCboStream.Read()))); break; case OC_PARAM_SHORT: mOutStream.Print("%" BOND_PRId32, int32_t(ReadValue16().mShort)); break; case OC_PARAM_USHORT: mOutStream.Print("%" BOND_PRId32, uint32_t(ReadValue16().mUShort)); break; case OC_PARAM_INT: { const size_t valueIndex = ReadValue16().mUShort; const int32_t value = mValue32Table[valueIndex].mInt; mOutStream.Print("%" BOND_PRId32, value); } break; case OC_PARAM_VAL32: { const size_t valueIndex = ReadValue16().mUShort; const uint32_t value = mValue32Table[valueIndex].mUInt; mOutStream.Print("0x%" BOND_PRIx32, value); } break; case OC_PARAM_VAL64: { const size_t valueIndex = ReadValue16().mUShort; const uint64_t value = mValue64Table[valueIndex].mULong; mOutStream.Print("0x%" BOND_PRIx64, value); } break; case OC_PARAM_OFF16: { const int32_t offset = ReadValue16().mShort; const uint32_t baseAddress = uint32_t(GetPosition() - codeStart); mOutStream.Print("%" BOND_PRId32 " (%" BOND_PRIu32 ")", offset, baseAddress + offset); } break; case OC_PARAM_OFF32: { const size_t offsetIndex = ReadValue16().mUShort; const int32_t offset = mValue32Table[offsetIndex].mInt; const uint32_t baseAddress = uint32_t(GetPosition() - codeStart); mOutStream.Print("%" BOND_PRId32 " (%" BOND_PRIu32 ")", offset, baseAddress + offset); } break; case OC_PARAM_STRING: { const size_t stringIndex = ReadValue16().mUShort; WriteAbbreviatedString(mStringTable[stringIndex]); } break; case OC_PARAM_NAME: { const size_t nameIndex = ReadValue16().mUShort; mQualifiedNameTable[nameIndex].PrintTo(mOutStream); } break; case OC_PARAM_LOOKUPSWITCH: { Skip(AlignUpDelta(GetPosition() - codeStart, sizeof(Value32))); const int32_t defaultOffset = ReadValue32().mInt; const uint32_t numMatches = ReadValue32().mUInt; const size_t tableSize = numMatches * 2 * sizeof(Value32); const int32_t baseAddress = int32_t(GetPosition() + tableSize - codeStart); mOutStream.Print("\n%16s: %" BOND_PRId32 " (%" BOND_PRIu32 ")", "default", defaultOffset, baseAddress + defaultOffset); for (uint32_t i = 0; i < numMatches; ++i) { const int32_t match = ReadValue32().mInt; const int32_t offset = ReadValue32().mInt; mOutStream.Print("\n%16" BOND_PRId32 ": %" BOND_PRId32 " (%" BOND_PRIu32 ")", match, offset, baseAddress + offset); } } break; case OC_PARAM_TABLESWITCH: { Skip(AlignUpDelta(GetPosition() - codeStart, sizeof(Value32))); const int32_t defaultOffset = ReadValue32().mInt; const int32_t minMatch = ReadValue32().mInt; const int32_t maxMatch = ReadValue32().mInt; const uint32_t numMatches = maxMatch - minMatch + 1; const size_t tableSize = numMatches * sizeof(Value32); const int32_t baseAddress = int32_t(GetPosition() + tableSize - codeStart); mOutStream.Print("\n%16s: %" BOND_PRId32 " (%" BOND_PRIu32 ")", "default", defaultOffset, baseAddress + defaultOffset); for (uint32_t i = 0; i < numMatches; ++i) { const int32_t match = minMatch + i; const int32_t offset = ReadValue32().mInt; mOutStream.Print("\n%16" BOND_PRId32 ": %" BOND_PRId32 " (%" BOND_PRIu32 ")", match, offset, baseAddress + offset); } } break; } mOutStream.Print("\n"); } // Disassemble the optional metadata blob. if (GetPosition() < blobEnd) { DisassembleBlob(); } }
static bool ExtractProcInfoFromFde(const libunwindInfo* info, unw_word_t* addrp, unw_proc_info_t *pip, int need_unwind_info) { unw_word_t addr = *addrp, fdeEndAddr, cieOffsetAddr, cieAddr; uint32_t value32; uint64_t value64; if (!ReadValue32(info, &addr, &value32)) { return false; } if (value32 != 0xffffffff) { int32_t cieOffset = 0; // In some configurations, an FDE with a 0 length indicates the end of the FDE-table if (value32 == 0) { return false; } // the FDE is in the 32-bit DWARF format */ *addrp = fdeEndAddr = addr + value32; cieOffsetAddr = addr; if (!ReadValue32(info, &addr, (uint32_t*)&cieOffset)) { return false; } // Ignore CIEs (happens during linear search) if (cieOffset == 0) { return true; } // DWARF says that the CIE_pointer in the FDE is a .debug_frame-relative offset, // but the GCC-generated .eh_frame sections instead store a "pcrelative" offset, // which is just as fine as it's self-contained cieAddr = cieOffsetAddr - cieOffset; } else { int64_t cieOffset = 0; // the FDE is in the 64-bit DWARF format */ if (!ReadValue64(info, &addr, (uint64_t*)&value64)) { return false; } *addrp = fdeEndAddr = addr + value64; cieOffsetAddr = addr; if (!ReadValue64(info, &addr, (uint64_t*)&cieOffset)) { return false; } // Ignore CIEs (happens during linear search) if (cieOffset == 0) { return true; } // DWARF says that the CIE_pointer in the FDE is a .debug_frame-relative offset, // but the GCC-generated .eh_frame sections instead store a "pcrelative" offset, // which is just as fine as it's self-contained cieAddr = (unw_word_t)((uint64_t)cieOffsetAddr - cieOffset); } dwarf_cie_info_t dci; if (!ParseCie(info, cieAddr, &dci)) { return false; } unw_word_t ipStart, ipRange; if (!ReadEncodedPointer(info, &addr, dci.fde_encoding, UINTPTR_MAX, &ipStart)) { return false; } // IP-range has same encoding as FDE pointers, except that it's always an absolute value uint8_t ipRangeEncoding = dci.fde_encoding & DW_EH_PE_FORMAT_MASK; if (!ReadEncodedPointer(info, &addr, ipRangeEncoding, UINTPTR_MAX, &ipRange)) { return false; } pip->start_ip = ipStart; pip->end_ip = ipStart + ipRange; pip->handler = dci.handler; unw_word_t augmentationSize, augmentationEndAddr; if (dci.sized_augmentation) { if (!ReadULEB128(info, &addr, &augmentationSize)) { return false; } augmentationEndAddr = addr + augmentationSize; } // Read language specific data area address if (!ReadEncodedPointer(info, &addr, dci.lsda_encoding, pip->start_ip, &pip->lsda)) { return false; } // Now fill out the proc info if requested if (need_unwind_info) { if (dci.have_abi_marker) { if (!ReadValue16(info, &addr, &dci.abi)) { return false; } if (!ReadValue16(info, &addr, &dci.tag)) { return false; } } if (dci.sized_augmentation) { dci.fde_instr_start = augmentationEndAddr; } else { dci.fde_instr_start = addr; } dci.fde_instr_end = fdeEndAddr; pip->format = UNW_INFO_FORMAT_TABLE; pip->unwind_info_size = sizeof(dci); pip->unwind_info = malloc(sizeof(dci)); if (pip->unwind_info == nullptr) { return -UNW_ENOMEM; } memcpy(pip->unwind_info, &dci, sizeof(dci)); } return true; }
static bool ReadEncodedPointer(const libunwindInfo* info, unw_word_t* addr, unsigned char encoding, unw_word_t funcRel, unw_word_t* valp) { unw_word_t initialAddr = *addr; uint16_t value16; uint32_t value32; uint64_t value64; unw_word_t value; if (encoding == DW_EH_PE_omit) { *valp = 0; return true; } else if (encoding == DW_EH_PE_aligned) { int size = sizeof(unw_word_t); *addr = (initialAddr + size - 1) & -size; return ReadPointer(info, addr, valp); } switch (encoding & DW_EH_PE_FORMAT_MASK) { case DW_EH_PE_ptr: if (!ReadPointer(info, addr, &value)) { return false; } break; case DW_EH_PE_uleb128: if (!ReadULEB128(info, addr, &value)) { return false; } break; case DW_EH_PE_sleb128: if (!ReadSLEB128(info, addr, &value)) { return false; } break; case DW_EH_PE_udata2: if (!ReadValue16(info, addr, &value16)) { return false; } value = value16; break; case DW_EH_PE_udata4: if (!ReadValue32(info, addr, &value32)) { return false; } value = value32; break; case DW_EH_PE_udata8: if (!ReadValue64(info, addr, &value64)) { return false; } value = value64; break; case DW_EH_PE_sdata2: if (!ReadValue16(info, addr, &value16)) { return false; } value = (int16_t)value16; break; case DW_EH_PE_sdata4: if (!ReadValue32(info, addr, &value32)) { return false; } value = (int32_t)value32; break; case DW_EH_PE_sdata8: if (!ReadValue64(info, addr, &value64)) { return false; } value = (int64_t)value64; break; default: ASSERT("ReadEncodedPointer: invalid encoding format %x\n", encoding); return false; } // 0 is a special value and always absolute if (value == 0) { *valp = 0; return true; } switch (encoding & DW_EH_PE_APPL_MASK) { case DW_EH_PE_absptr: break; case DW_EH_PE_pcrel: value += initialAddr; break; case DW_EH_PE_funcrel: _ASSERTE(funcRel != UINTPTR_MAX); value += funcRel; break; case DW_EH_PE_textrel: case DW_EH_PE_datarel: default: ASSERT("ReadEncodedPointer: invalid application type %x\n", encoding); return false; } if (encoding & DW_EH_PE_indirect) { unw_word_t indirect_addr = value; if (!ReadPointer(info, &indirect_addr, &value)) { return false; } } *valp = value; return true; }
CboValidator::Result CboValidatorCore::Validate() { const uint32_t magicNumber = ReadValue32().mUInt; const uint32_t majorVersion = ReadValue16().mUShort; const uint32_t minorVersion = ReadValue16().mUShort; const uint16_t flags = ReadValue16().mUShort; const size_t tableStart = GetPosition(); const size_t tableSize = ReadValue32().mUInt; const size_t value32Count = ReadValue16().mUShort; const size_t value64Count = ReadValue16().mUShort; const size_t stringCount = ReadValue16().mUShort; const size_t qualifiedNameCount = ReadValue16().mUShort; BOND_ASSERT_FORMAT(magicNumber == MAGIC_NUMBER, ("CBO file contains invalid magic number: 0x%" BOND_PRIx32 ".", magicNumber)); BOND_ASSERT_FORMAT(IsCBOFormatLoadable(majorVersion, minorVersion), ("Unsupported CBO file format version: %" BOND_PRIu32 ".%" BOND_PRIu32 ". Only CBO file format versions in the range %" BOND_PRIu32 ".%" BOND_PRIu32 " through %" BOND_PRIu32 ".XX are supported.", majorVersion, minorVersion, CBO_MIN_SUPPORTED_MAJOR_VERSION, CBO_MIN_SUPPORTED_MINOR_VERSION, CBO_MAX_SUPPORTED_MAJOR_VERSION)); mResult.mMajorVersion = majorVersion; mResult.mMinorVersion = minorVersion; mResult.mPointerSize = DecodePointerSize(flags); mResult.mValue32Count = value32Count; mResult.mValue64Count = value64Count; mResult.mStringCount = stringCount; mResult.mQualifiedNameCount = qualifiedNameCount; const size_t valueSize = (value32Count * sizeof(Value32)) + (value64Count * sizeof(Value64)); AssertBytesRemaining(tableSize - (GetPosition() - tableStart)); AssertBytesRemaining(valueSize); mValue32TableOffset = GetPosition(); Skip((value32Count * sizeof(Value32)) + (value64Count * sizeof(Value64))); size_t stringByteCount = 0; for (size_t i = 0; i < stringCount; ++i) { AssertBytesRemaining(sizeof(Value16)); const size_t stringLength = ReadValue16().mUShort; stringByteCount += stringLength; AssertBytesRemaining(stringLength); if (stringLength == BOND_BLOB_ID_LENGTH) { char str[BOND_BLOB_ID_LENGTH]; mStream.Read(str, BOND_BLOB_ID_LENGTH); if (StringEqual(str, BOND_BLOB_ID_LENGTH, BOND_LIST_BLOB_ID, BOND_BLOB_ID_LENGTH)) { mResult.mListBlobIdIndex = i; } else if (StringEqual(str, BOND_BLOB_ID_LENGTH, BOND_FUNCTION_BLOB_ID, BOND_BLOB_ID_LENGTH)) { mResult.mFunctionBlobIdIndex = i; } else if (StringEqual(str, BOND_BLOB_ID_LENGTH, BOND_DATA_BLOB_ID, BOND_BLOB_ID_LENGTH)) { mResult.mDataBlobIdIndex = i; } } else { mStream.AddOffset(Stream::pos_t(stringLength)); } } mResult.mStringByteCount = stringByteCount; size_t qualifiedNameElementCount = 0; for (size_t i = 0; i < qualifiedNameCount; ++i) { AssertBytesRemaining(sizeof(Value16)); const size_t numElements = ReadValue16().mUShort; AssertBytesRemaining(numElements * sizeof(Value16)); qualifiedNameElementCount += numElements; if (numElements == 0) { mResult.mStaticInitializerNameIndex = i; } for (size_t j = 0; j < numElements; ++j) { const size_t elementIndex = ReadValue16().mUShort; if (elementIndex >= stringCount) { CboIsInvalid(); } } } mResult.mQualifiedNameElementCount = qualifiedNameElementCount; const size_t tableEnd = tableStart + tableSize; if (GetPosition() != tableEnd) { CboIsInvalid(); return mResult; } mResult.mDataAlignment = size_t(BOND_SLOT_SIZE); ValidateBlob(); if (GetPosition() != mStreamEnd) { CboIsInvalid(); } return mResult; }
void CboValidatorCore::ValidateFunctionBlob(size_t blobEnd) { const size_t functionNameIndex = ValidateQualifiedNameIndex(); ValidateSizeAndType(); ValidateParamListSignature(); AssertBytesRemaining(6 * sizeof(Value32)); const uint32_t argSize = ReadValue32().mUInt; const uint32_t packedArgSize = ReadValue32().mUInt; const uint32_t localSize = ReadValue32().mUInt; Skip(sizeof(Value32)); // Ignore the stack size. const uint32_t framePointerAlignment = ReadValue32().mUInt; const size_t codeSize = ReadValue32().mUInt; if ((packedArgSize > argSize) || ((argSize % BOND_SLOT_SIZE) != 0) || ((packedArgSize % BOND_SLOT_SIZE) != 0) || ((localSize % BOND_SLOT_SIZE) != 0) || ((framePointerAlignment % BOND_SLOT_SIZE) != 0)) { FunctionIsInvalid(); } if (functionNameIndex == mResult.mStaticInitializerNameIndex) { ++mResult.mStaticInitializerCount; } else { ++mResult.mFunctionCount; } mResult.mCodeByteCount += AlignUp(codeSize, sizeof(Value32)); AssertBytesRemaining(codeSize); const size_t codeStart = GetPosition(); const size_t codeEnd = codeStart + codeSize; // Do a validation pass on the bytecode and ensure everything is converted to the correct endianness. while (GetPosition() < codeEnd) { const OpCode opCode = static_cast<OpCode>(mStream.Read()); const OpCodeParam param = GetOpCodeParamType(opCode); switch (param) { case OC_PARAM_NONE: break; case OC_PARAM_CHAR: case OC_PARAM_UCHAR: AssertBytesRemaining(1); Skip(1); break; case OC_PARAM_UCHAR_CHAR: case OC_PARAM_SHORT: case OC_PARAM_USHORT: AssertBytesRemaining(sizeof(Value16)); ReadValue16(); break; case OC_PARAM_INT: case OC_PARAM_VAL32: { AssertBytesRemaining(sizeof(Value16)); const size_t valueIndex = ReadValue16().mUShort; if (valueIndex >= mResult.mValue32Count) { CodeIsInvalid(); } } break; case OC_PARAM_VAL64: { AssertBytesRemaining(sizeof(Value16)); const size_t valueIndex = ReadValue16().mUShort; if (valueIndex >= mResult.mValue64Count) { CodeIsInvalid(); } } break; case OC_PARAM_OFF16: { AssertBytesRemaining(sizeof(Value16)); const int32_t offset = ReadValue16().mShort; const int32_t baseAddress = int32_t(GetPosition() - codeStart); const int32_t targetAddress = baseAddress + offset; if ((targetAddress < 0) || (uint32_t(targetAddress) > codeSize)) { CodeIsInvalid(); } } break; case OC_PARAM_OFF32: { AssertBytesRemaining(sizeof(Value16)); const size_t offsetIndex = ReadValue16().mUShort; if (offsetIndex >= mResult.mValue32Count) { CodeIsInvalid(); } else { const int32_t offset = ReadValue32TableAt(offsetIndex).mInt; const int32_t baseAddress = int32_t(GetPosition() - codeStart); const int32_t targetAddress = baseAddress + offset; if ((targetAddress < 0) || (uint32_t(targetAddress) > codeSize)) { CodeIsInvalid(); } } } break; case OC_PARAM_STRING: { AssertBytesRemaining(sizeof(Value16)); const size_t stringIndex = ReadValue16().mUShort; if (stringIndex >= mResult.mStringCount) { CodeIsInvalid(); } } break; case OC_PARAM_NAME: { AssertBytesRemaining(sizeof(Value16)); const size_t nameIndex = ReadValue16().mUShort; if (nameIndex >= mResult.mQualifiedNameCount) { CodeIsInvalid(); } } break; case OC_PARAM_LOOKUPSWITCH: { Skip(AlignUpDelta(GetPosition() - codeStart, sizeof(Value32))); AssertBytesRemaining(2 * sizeof(Value32)); const int32_t defaultOffset = ReadValue32().mInt; const uint32_t numMatches = ReadValue32().mUInt; const size_t tableSize = numMatches * 2 * sizeof(Value32); const int32_t baseAddress = int32_t(GetPosition() + tableSize - codeStart); const int32_t defaultAddress = baseAddress + defaultOffset; AssertBytesRemaining(tableSize); if ((defaultAddress < 0) || (uint32_t(defaultAddress) > codeSize)) { CodeIsInvalid(); } for (uint32_t i = 0; i < numMatches; ++i) { // Skip the match. Skip(sizeof(Value32)); const int32_t offset = ReadValue32().mInt; const int32_t targetAddress = baseAddress + offset; if ((targetAddress < 0) || (uint32_t(targetAddress) > codeSize)) { CodeIsInvalid(); } } } break; case OC_PARAM_TABLESWITCH: { Skip(AlignUpDelta(GetPosition() - codeStart, sizeof(Value32))); AssertBytesRemaining(3 * sizeof(Value32)); const int32_t defaultOffset = ReadValue32().mInt; const int32_t minMatch = ReadValue32().mInt; const int32_t maxMatch = ReadValue32().mInt; const uint32_t numMatches = uint32_t(maxMatch - minMatch + 1); const size_t tableSize = numMatches * sizeof(Value32); const int32_t baseAddress = int32_t(GetPosition() + tableSize - codeStart); const int32_t defaultAddress = baseAddress + defaultOffset; AssertBytesRemaining(tableSize); if (minMatch > maxMatch) { CodeIsInvalid(); } if ((defaultAddress < 0) || (uint32_t(defaultAddress) > codeSize)) { CodeIsInvalid(); } for (size_t i = 0; i < numMatches; ++i) { const int32_t offset = ReadValue32().mInt; const int32_t targetAddress = baseAddress + offset; if ((targetAddress < 0) || (uint32_t(targetAddress) > codeSize)) { CodeIsInvalid(); } } } break; } } // Validate the optional metadata blob. if (GetPosition() < blobEnd) { ValidateBlob(); } }