fix(relinker): validate scan ranges before narrowing memory offsets

This commit is contained in:
boykopovar 2026-10-13 11:01:48 +03:00
parent 3e658db4b6
commit e1ef33047c
4 changed files with 16 additions and 11 deletions

View File

@ -296,10 +296,10 @@ void Amd64OnlyConverter::_convertSegment(
std::uint64_t completed,
std::uint64_t total
) const {
if (ph.Offset > fileBytes.size() || ph.FileSize > fileBytes.size() - ph.Offset)
throw CodegenException("Code segment exceeds the file", ph.Offset);
const auto segOffset = static_cast<std::size_t>(ph.Offset);
const auto segSize = static_cast<std::size_t>(ph.FileSize);
if (segOffset > fileBytes.size() || segSize > fileBytes.size() - segOffset)
throw CodegenException("Code segment exceeds the file", ph.Offset);
std::vector<std::uint8_t> seg(
fileBytes.begin() + static_cast<std::ptrdiff_t>(segOffset),
@ -318,9 +318,9 @@ void Amd64OnlyConverter::_convertSegment(
if (substitution.has_value())
pending.push_back({index, match, std::move(*substitution)});
}
if (progress) progress(completed + 2 * segSize, total);
if (progress) progress(completed + std::uint64_t{2} * segSize, total);
if (pending.empty()) {
if (progress) progress(completed + 3 * segSize, total);
if (progress) progress(completed + std::uint64_t{3} * segSize, total);
return;
}
@ -337,7 +337,7 @@ void Amd64OnlyConverter::_convertSegment(
std::size_t processed = 0;
for (const auto& item : pending) {
if (progress && processed != 0 && processed % 256 == 0) progress(completed + 2 * segSize + item.Instruction.Offset, total);
if (progress && processed != 0 && processed % 256 == 0) progress(completed + std::uint64_t{2} * segSize + item.Instruction.Offset, total);
++processed;
if (consumed.contains(item.Index))
continue;
@ -439,7 +439,7 @@ void Amd64OnlyConverter::_convertSegment(
if (seg.size() != segSize)
throw CodegenException("Code segment size changed during Intel conversion", ph.Offset);
std::copy(seg.begin(), seg.end(), fileBytes.begin() + static_cast<std::ptrdiff_t>(segOffset));
if (progress) progress(completed + 3 * segSize, total);
if (progress) progress(completed + std::uint64_t{3} * segSize, total);
}
ConvertResult Amd64OnlyConverter::Convert(

View File

@ -3,6 +3,7 @@
#include <codegen/x86/X64InstructionDecoder.hpp>
#include <algorithm>
#include <memory>
#include <limits>
#include <string>
namespace Codegen {
@ -17,14 +18,16 @@ private:
};
std::vector<InstructionMatch> InstructionScanner::ScanCodeSection(const std::vector<std::uint8_t>& codeSection, const Domain::FileByteOffset codeSectionOffset, const Domain::FileByteOffset codeSectionSize) const {
const std::size_t limit = std::min(codeSection.size(), static_cast<std::size_t>(codeSectionSize));
if (codeSectionSize > codeSection.size()) throw CodegenException("Code section exceeds the input buffer", codeSectionOffset);
if (codeSectionSize > std::numeric_limits<Domain::FileByteOffset>::max() - codeSectionOffset) throw CodegenException("Code section offset overflow", codeSectionOffset);
const auto limit = static_cast<std::size_t>(codeSectionSize);
const X64InstructionDecoder decoder;
std::vector<InstructionMatch> matches;
std::size_t i = 0;
if (progress) progress(0, limit);
std::size_t nextReport = 65536;
std::size_t lastReport = 0;
while (i < limit) {
const std::uint8_t* cursor = codeSection.data() + i;
const std::size_t available = limit - i;
@ -40,9 +43,9 @@ std::vector<InstructionMatch> InstructionScanner::ScanCodeSection(const std::vec
matches.push_back({codeSectionOffset + i, length});
i += length;
if (progress && i >= nextReport) {
if (progress && i - lastReport >= 65536) {
progress(i, limit);
nextReport = i + 65536;
lastReport = i;
}
}

View File

@ -162,7 +162,8 @@ private:
};
std::vector<std::size_t> SyscallScanner::ScanCodeSectionForSyscalls(const std::vector<std::uint8_t>& codeSection, const FileByteOffset codeSectionOffset, const FileByteOffset codeSectionSize, std::string_view input) {
const std::size_t limit = std::min(codeSection.size(), static_cast<std::size_t>(codeSectionSize));
if (codeSectionSize > codeSection.size()) throw RelinkerException("Code section exceeds the input buffer", codeSectionOffset);
const auto limit = static_cast<std::size_t>(codeSectionSize);
const auto scanner = Codegen::MakeInstructionScanner([&](auto completed, auto total) { progress.Report({Domain::ProgressStage::Decode, std::string(input), completed, total}); });
const auto matches = scanner->ScanCodeSection(codeSection, codeSectionOffset, codeSectionSize);

View File

@ -125,6 +125,7 @@ void scanning() {
previous = event.Completed;
}
require(previous == progress.Events.back().Total, "Instruction conversion progress did not finish");
reject([&] { scanner->ScanCodeSectionForSyscalls(code, 0, (std::uint64_t{1} << 32) + code.size()); });
code[70000] = 0x0f;
code[70001] = 0x05;
progress.Events.clear();