Merge pull request #8689 from MarcZX10/codex/reject-large-non-elf-early

fix(relinker): reject large non-ELF inputs before allocation
This commit is contained in:
Adrià authored and GitHub committed 2026-10-21 21:12:48 +02:00
commit 3f23f2ae65
6 files changed
+51 -1

No files matched your search

@@ -2,11 +2,13 @@
#define IO_FILEREADER_HPP
#include <io/IFileReader.hpp>
#include <cstddef>
namespace Io {
class FileReader : public IFileReader {
public:
std::vector<std::uint8_t> ReadPrefix(const std::string& path, std::size_t size) const;
std::vector<std::uint8_t> Read(const std::string& path) override;
};
+20
View File
@@ -3,9 +3,29 @@
#include <domain/Types.hpp>
#include <filesystem>
#include <fstream>
#include <limits>
namespace Io {
std::vector<std::uint8_t> FileReader::ReadPrefix(const std::string& path, const std::size_t size) const {
if (size > static_cast<std::size_t>(std::numeric_limits<std::streamsize>::max()))
throw Domain::RelinkerException("File prefix is too large: " + path);
const auto native = NativePath(path);
std::error_code error;
if (!std::filesystem::is_regular_file(native, error))
throw Domain::RelinkerException("Cannot open file: " + path);
std::ifstream f(native, std::ios::binary);
if (!f)
throw Domain::RelinkerException("Cannot open file: " + path);
std::vector<std::uint8_t> buf(size);
f.read(reinterpret_cast<char*>(buf.data()), static_cast<std::streamsize>(size));
const auto count = f.gcount();
if (f.bad())
throw Domain::RelinkerException("Cannot read file: " + path);
buf.resize(static_cast<std::size_t>(count));
return buf;
}
std::vector<std::uint8_t> FileReader::Read(const std::string& path) {
const auto native = NativePath(path);
std::error_code error;
+1
View File
@@ -40,6 +40,7 @@ struct ConvertedExecutable {
ConvertedExecutable Convert(const Cli::Args& args, const std::string& inputPath, const std::string& absPath, const bool mainExecutable, const std::string& targetName, const std::vector<Relinker::GamePatch::PatchEntry>& patches, Cli::ConsoleProgress& progress) {
Io::FileReader fileReader;
Relinker::ConversionInput input;
Relinker::ElfReader(fileReader.ReadPrefix(inputPath, 0x40)).ValidateMagic();
input.Executable = fileReader.Read(inputPath);
input.InputPath = std::filesystem::absolute(Io::NativePath(inputPath)).lexically_normal();
input.OutputPath = Io::NativePath(absPath);
@@ -10,6 +10,7 @@ class ElfReader : public IElfReader {
public:
explicit ElfReader(std::vector<std::uint8_t> fileBuffer);
void ValidateMagic() const;
ElfHeader ReadHeader() const override;
std::vector<ProgramHeader> ReadProgramHeaders() const override;
std::vector<SectionHeader> ReadSectionHeaders() const override;
@@ -76,7 +76,7 @@ std::uint64_t ElfReader::_readU64At(FileByteOffset fileByteOffset) const {
return value;
}
ElfHeader ElfReader::ReadHeader() const {
void ElfReader::ValidateMagic() const {
if (_fileBuffer.size() < 0x40) {
throw RelinkerException("File too small for ELF header");
}
@@ -90,6 +90,10 @@ ElfHeader ElfReader::ReadHeader() const {
_fileBuffer[2] != 'L' || _fileBuffer[3] != 'F') {
throw RelinkerException("Invalid ELF magic number: " + formatMagic());
}
}
ElfHeader ElfReader::ReadHeader() const {
ValidateMagic();
if (_fileBuffer[4] != 2 || _fileBuffer[5] != 1 || _fileBuffer[6] != 1)
throw RelinkerException("Expected little-endian ELF64 version 1");
if (_readU16At(0x12) != 62)
@@ -1,6 +1,7 @@
"""Reject inputs that are not plain ELF files with a message that names what was read."""
from pathlib import Path
import platform
import subprocess
import sys
import tempfile
@@ -23,6 +24,27 @@ def main():
convert("eboot.self.kernel", b"\x54\x14\xf5\xee" + bytes(0x1000), "The input is a SELF container, not an ELF")
convert("eboot.pkg", b"\x7fCNT" + bytes(0x1000), "Invalid ELF magic number: 7f 43 4e 54")
convert("eboot.short", b"\x7fEL", "File too small for ELF header")
unsupported_version = bytearray(64)
unsupported_version[:4] = b"\x7fELF"
unsupported_version[4:7] = b"\x02\x01\x01"
unsupported_version[0x12:0x14] = b"\x3e\x00"
convert("eboot.unsupported-version", unsupported_version, "Unsupported ELF object version: expected 1")
if platform.system() == "Linux":
import resource
source = work / "large-unsupported.pkg"
output = work / "large-unsupported.out"
with source.open("wb") as stream:
stream.write(b"\x7fFIH")
stream.truncate(8 * 1024**3)
def limit_address_space():
resource.setrlimit(resource.RLIMIT_AS, (512 * 1024**2, 512 * 1024**2))
resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
result = subprocess.run([str(relinker), "--skip-sce-module", str(source), str(output)],
capture_output=True, text=True, timeout=20, preexec_fn=limit_address_space)
assert result.returncode == 2 and "Invalid ELF magic number: 7f 46 49 48" in result.stderr and not output.exists(), result
print("Input magic diagnostics tests passed")