diff --git a/core/libs/prx/libScePngDec/Export.cpp b/core/libs/prx/libScePngDec/Export.cpp index 506c66b066..c4404f2e40 100644 --- a/core/libs/prx/libScePngDec/Export.cpp +++ b/core/libs/prx/libScePngDec/Export.cpp @@ -145,6 +145,8 @@ int32_t APS5_VABI scePngDecDecode(void* handle, const PngDecDecodeParam* param, const std::optional header = Decoder::Png::ParseHeader(png); if (!header) return SCE_PNG_DEC_ERROR_INVALID_DATA; if (imageInfo) fillImageInfo(*header, imageInfo); + const bool packedDimensions = header->width <= MAX_PACKED_DIMENSION && header->height <= MAX_PACKED_DIMENSION; + if (!imageInfo && !packedDimensions) return SCE_PNG_DEC_ERROR_INVALID_ADDR; const std::uint64_t rowSize = static_cast(header->width) * BYTES_PER_PIXEL; const std::uint64_t pitch = param->image_pitch == 0 ? rowSize : param->image_pitch; if (param->image_pitch != 0 && (pitch < rowSize || (pitch & 3u) != 0)) return SCE_PNG_DEC_ERROR_INVALID_PARAM; @@ -176,7 +178,7 @@ int32_t APS5_VABI scePngDecDecode(void* handle, const PngDecDecodeParam* param, } } - if (header->width > MAX_PACKED_DIMENSION || header->height > MAX_PACKED_DIMENSION) return 0; + if (!packedDimensions) return 0; return static_cast(header->width << 16 | header->height); } diff --git a/core/libs/tests/GuestPngDec.cpp b/core/libs/tests/GuestPngDec.cpp index ef3149d724..120be43fdc 100644 --- a/core/libs/tests/GuestPngDec.cpp +++ b/core/libs/tests/GuestPngDec.cpp @@ -149,6 +149,70 @@ static void Check16BitImage(void* limitedHandle, void* enabledHandle, std::span< } } +static void CheckDimensionReporting(std::uint32_t width, std::uint32_t height) { + constexpr std::int32_t invalidAddr = static_cast(0x80690001); + constexpr std::int32_t invalidSize = static_cast(0x80690002); + constexpr std::int32_t invalidParam = static_cast(0x80690003); + constexpr std::int32_t invalidData = static_cast(0x80690010); + std::vector rgb(static_cast(width) * height * 3); + for (std::size_t i = 0; i < rgb.size(); ++i) rgb[i] = static_cast(i * 13 + 7); + const auto png = Decoder::Png::Encode(rgb, width, height, 3); + const PngDecCreateParam create{sizeof(PngDecCreateParam), 0, width}; + const auto memorySize = scePngDecQueryMemorySize(&create); + Require(memorySize > 0); + std::vector memory(memorySize); + void* handle = nullptr; + Require(scePngDecCreate(&create, memory.data(), memorySize, &handle) == 0); + const bool packed = width <= 32767 && height <= 32767; + const auto result = packed ? static_cast(width << 16 | height) : 0; + for (const std::uint16_t format : {0, 1}) { + for (const std::uint32_t padding : {0, 8}) { + const std::uint32_t pitch = width * 4 + padding; + const std::uint32_t size = pitch * height; + std::vector image(size + 16, 0xCC); + auto decode = DecodeParam(png, image); + decode.pixel_format = format; + decode.image_pitch = padding == 0 ? 0 : pitch; + decode.image_mem_size = size; + Require(scePngDecDecode(handle, &decode, nullptr) == (packed ? result : invalidAddr)); + if (!packed) Require(std::all_of(image.begin(), image.end(), [](auto byte) { return byte == 0xCC; })); + PngDecImageInfo info{}; + Require(scePngDecDecode(handle, &decode, &info) == result); + Require(info.image_width == width && info.image_height == height && info.color_space == 3 && info.bit_depth == 8 && info.image_flag == 0); + for (std::uint32_t y = 0; y < height; ++y) { + for (std::uint32_t x = 0; x < width; ++x) { + for (std::uint32_t channel = 0; channel < 3; ++channel) { + const auto inputChannel = format == 0 ? channel : 2 - channel; + Require(image[y * pitch + x * 4 + channel] == rgb[(static_cast(y) * width + x) * 3 + inputChannel]); + } + Require(image[y * pitch + x * 4 + 3] == 0x77); + } + for (std::uint32_t offset = width * 4; offset < pitch; ++offset) Require(image[y * pitch + offset] == 0xCC); + } + Require(std::all_of(image.begin() + size, image.end(), [](auto byte) { return byte == 0xCC; })); + if (packed) continue; + std::fill(image.begin(), image.end(), 0xCC); + decode.image_mem_size = 1; + Require(scePngDecDecode(handle, &decode, nullptr) == invalidAddr); + Require(scePngDecDecode(handle, &decode, &info) == (padding == 0 ? invalidSize : invalidParam)); + decode.image_mem_size = size; + decode.image_pitch = 1; + Require(scePngDecDecode(handle, &decode, nullptr) == invalidAddr); + Require(scePngDecDecode(handle, &decode, &info) == invalidParam); + decode.image_mem_size = 0; + Require(scePngDecDecode(handle, &decode, nullptr) == invalidSize); + decode.image_mem_size = size; + decode.pixel_format = 2; + Require(scePngDecDecode(handle, &decode, nullptr) == invalidParam); + decode.pixel_format = format; + decode.png_mem_size = 8; + Require(scePngDecDecode(handle, &decode, nullptr) == invalidData); + Require(std::all_of(image.begin(), image.end(), [](auto byte) { return byte == 0xCC; })); + } + } + Require(scePngDecDelete(handle) == 0); +} + int main() { constexpr std::int32_t invalidAddr = static_cast(0x80690001); constexpr std::int32_t invalidSize = static_cast(0x80690002); @@ -352,5 +416,9 @@ int main() { Require(scePngDecDelete(handle) == invalidHandle); Require(scePngDecDecode(handle, &decode, &info) == invalidHandle); + for (const std::uint32_t dimension : {32767, 32768, 65536}) { + CheckDimensionReporting(dimension, 1); + CheckDimensionReporting(1, dimension); + } return 0; } diff --git a/docs/dev/debt/libScePngDec.md b/docs/dev/debt/libScePngDec.md index 5ab2fe8ca1..260202e2bf 100644 --- a/docs/dev/debt/libScePngDec.md +++ b/docs/dev/debt/libScePngDec.md @@ -3,7 +3,7 @@ ### Functional - [libScePngDec](../../../core/libs/prx/libScePngDec/Export.cpp): the supplied module confirms the work-memory size formula, 16-byte handle alignment, the first 24 bytes of its context, and Create/QueryMemorySize validation order. The host now exposes these values but still allocates decoder working storage on the host instead of using the guest work region. -- [scePngDecDecode](../../../core/libs/prx/libScePngDec/Export.cpp): the original's accepted pixel format 132 remains unsupported and throws. The input bit-depth limit and 16-to-8-bit conversion follow [Prosper's PS5 3.20 module analysis](https://github.com/mattias800/prosper/blob/550886e3677e4a2ef81ad1603bf313f6a9696de2/prosper/src/hle/util/hle_pngdec.cpp#L412-L414), not console measurements here. Malformed-image diagnostics and decoder scratch-memory behavior are not fully matched to the supplied module. +- [scePngDecDecode](../../../core/libs/prx/libScePngDec/Export.cpp): the original's accepted pixel format 132 remains unsupported and throws. The input bit-depth limit and 16-to-8-bit conversion follow [Prosper's PS5 3.20 module analysis](https://github.com/mattias800/prosper/blob/550886e3677e4a2ef81ad1603bf313f6a9696de2/prosper/src/hle/util/hle_pngdec.cpp#L412-L414), and the large-dimension image-info requirement and its validation order follow [Prosper's PS5 3.20 module analysis](https://github.com/mattias800/prosper/blob/550886e3677e4a2ef81ad1603bf313f6a9696de2/prosper/src/hle/util/hle_pngdec.cpp#L385-L396), not console measurements here. Malformed-image diagnostics and decoder scratch-memory behavior are not fully matched to the supplied module. ### Unknown function info