Merge pull request #4608 from MiniRML/fix/relinker-icon-png-chunks
fix(relinker): validate icon PNG critical chunks and order
This commit is contained in:
commit
34cee1ef15
|
|
@ -30,6 +30,9 @@ void validatePng(const std::vector<std::uint8_t>& bytes, const std::filesystem::
|
|||
((color == 2 || color == 4 || color == 6) && (depth == 8 || depth == 16));
|
||||
if (!validDepth || bytes[26] != 0 || bytes[27] != 0 || bytes[28] > 1) fail("invalid IHDR encoding");
|
||||
bool hasData = false;
|
||||
bool dataStarted = false;
|
||||
bool dataEnded = false;
|
||||
bool hasPalette = false;
|
||||
for (std::size_t offset = 8; offset < bytes.size();) {
|
||||
if (bytes.size() - offset < 12) fail("truncated chunk");
|
||||
const auto length = readBigEndian(bytes, offset);
|
||||
|
|
@ -42,7 +45,20 @@ void validatePng(const std::vector<std::uint8_t>& bytes, const std::filesystem::
|
|||
}
|
||||
if ((crc ^ 0xffffffffu) != readBigEndian(bytes, offset + 8 + length)) fail("invalid chunk CRC");
|
||||
if (type == 0x49484452 && offset != 8) fail("duplicate IHDR");
|
||||
if (type == 0x49444154 && length != 0) hasData = true;
|
||||
if (type == 0x504c5445) {
|
||||
if (hasPalette || dataStarted || !length || length % 3 || length > 768 || color == 0 || color == 4 ||
|
||||
(color == 3 && length / 3 > (1u << depth))) fail("invalid PLTE");
|
||||
hasPalette = true;
|
||||
}
|
||||
if (type == 0x49444154) {
|
||||
if (dataEnded || (color == 3 && !hasPalette)) fail("invalid IDAT order or missing PLTE");
|
||||
dataStarted = true;
|
||||
if (length != 0) hasData = true;
|
||||
} else {
|
||||
if (dataStarted) dataEnded = true;
|
||||
if (!(type & 0x20000000u) && type != 0x49484452 && type != 0x504c5445 && type != 0x49454e44)
|
||||
fail("unknown critical chunk");
|
||||
}
|
||||
offset += length + 12;
|
||||
if (type == 0x49454e44) {
|
||||
if (length != 0 || offset != bytes.size() || !hasData) fail("invalid IEND or missing IDAT");
|
||||
|
|
|
|||
|
|
@ -27,9 +27,76 @@ std::uint32_t readU32(const std::vector<std::uint8_t>& data, const std::size_t o
|
|||
static_cast<std::uint32_t>(data[offset + 2]) << 16 | static_cast<std::uint32_t>(data[offset + 3]) << 24;
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> chunk(const std::string& type, const std::vector<std::uint8_t>& payload) {
|
||||
std::vector<std::uint8_t> result;
|
||||
const auto append = [&result](std::uint32_t value) {
|
||||
for (const auto shift : {24, 16, 8, 0}) result.push_back(static_cast<std::uint8_t>(value >> shift));
|
||||
};
|
||||
append(static_cast<std::uint32_t>(payload.size()));
|
||||
result.insert(result.end(), type.begin(), type.end());
|
||||
result.insert(result.end(), payload.begin(), payload.end());
|
||||
std::uint32_t crc = 0xffffffffu;
|
||||
for (std::size_t index = 4; index < result.size(); ++index) {
|
||||
crc ^= result[index];
|
||||
for (int bit = 0; bit < 8; ++bit) crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320u : 0u);
|
||||
}
|
||||
append(crc ^ 0xffffffffu);
|
||||
return result;
|
||||
}
|
||||
|
||||
void checkChunks() {
|
||||
const Elfpatcher::Windows::WindowsIconResourceBuilder builder;
|
||||
const auto check = [&](const std::string& name, const std::vector<std::vector<std::uint8_t>>& chunks, bool valid, const std::vector<std::uint8_t>& header = {}) {
|
||||
std::vector<std::uint8_t> image(Png.begin(), Png.begin() + (header.empty() ? 33 : 8));
|
||||
if (!header.empty()) {
|
||||
const auto ihdr = chunk("IHDR", header);
|
||||
image.insert(image.end(), ihdr.begin(), ihdr.end());
|
||||
}
|
||||
for (const auto& value : chunks) image.insert(image.end(), value.begin(), value.end());
|
||||
image.insert(image.end(), Png.end() - 12, Png.end());
|
||||
const std::filesystem::path path = std::filesystem::path(name) / "icon.png";
|
||||
std::vector<Elfpatcher::Windows::PeSection> sections;
|
||||
bool rejected = false;
|
||||
try { builder.Build(image, path, sections, 0x10000); }
|
||||
catch (const Domain::RelinkerException& error) {
|
||||
rejected = true;
|
||||
require(std::string(error.what()).find(path.string()) != std::string::npos, "Icon failure omitted the path");
|
||||
}
|
||||
require(rejected != valid, "Unexpected validation result for " + name);
|
||||
require(sections.size() == (valid ? 1u : 0u), "Unexpected section mutation for " + name);
|
||||
};
|
||||
const std::vector<std::uint8_t> data(Png.begin() + 41, Png.begin() + 54);
|
||||
const auto idat = chunk("IDAT", data);
|
||||
const auto note = chunk("tEXt", {'n', 0, 'v'});
|
||||
check("unknown-critical", {chunk("ABCD", {}), idat}, false);
|
||||
check("ancillary", {note, idat}, true);
|
||||
const auto first = chunk("IDAT", {data.begin(), data.begin() + 6});
|
||||
const auto last = chunk("IDAT", {data.begin() + 6, data.end()});
|
||||
check("consecutive", {first, last}, true);
|
||||
check("nonconsecutive", {first, note, last}, false);
|
||||
const auto palette = chunk("PLTE", {255, 0, 0});
|
||||
check("late-palette", {idat, palette}, false);
|
||||
check("duplicate-palette", {palette, palette, idat}, false);
|
||||
check("palette", {palette, idat}, true);
|
||||
check("empty-palette", {chunk("PLTE", {}), idat}, false);
|
||||
check("partial-palette", {chunk("PLTE", {0, 1, 2, 3}), idat}, false);
|
||||
check("large-palette", {chunk("PLTE", std::vector<std::uint8_t>(771)), idat}, false);
|
||||
std::vector<std::uint8_t> header(Png.begin() + 16, Png.begin() + 29);
|
||||
header[8] = 1;
|
||||
header[9] = 3;
|
||||
const auto indexed = chunk("IDAT", {0x78, 0x9c, 0x63, 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01});
|
||||
check("indexed", {palette, indexed}, true, header);
|
||||
check("indexed-missing-palette", {indexed}, false, header);
|
||||
check("indexed-large-palette", {chunk("PLTE", std::vector<std::uint8_t>(9)), indexed}, false, header);
|
||||
header[9] = 0;
|
||||
check("grayscale-palette", {palette, indexed}, false, header);
|
||||
}
|
||||
|
||||
void run() {
|
||||
const Elfpatcher::Windows::WindowsIconResourceBuilder builder;
|
||||
|
||||
checkChunks();
|
||||
|
||||
const std::filesystem::path iconPath = "valid/icon0.png";
|
||||
const std::uint32_t nextRva = 0x50000;
|
||||
std::vector<Elfpatcher::Windows::PeSection> sections;
|
||||
|
|
|
|||
Loading…
Reference in New Issue