提前拒绝高位深 PNG 解码

通过 image crate 的 ImageReader decoder 元数据在像素缓冲分配前拒绝 16-bit PNG。

继续使用同一外部 decoder 执行受限像素读取校验,不手写 PNG 解析器。
This commit is contained in:
2026-09-12 15:40:26 +08:00
parent 6235fa3298
commit 413707da33
+21 -5
View File
@@ -4,7 +4,7 @@ use axum::{
http::StatusCode,
};
use bytes::Bytes;
use image::{GenericImageView, ImageFormat, ImageReader};
use image::{ImageDecoder, ImageFormat, ImageReader};
use platform_image::{
GPT_IMAGE_2_2K_LONG_EDGE_THRESHOLD, RAW_IMAGE_MAX_EDGE, RAW_IMAGE_MAX_PIXELS,
RawImageEditImage, RawImageEditOptions, create_vector_engine_raw_image_edit,
@@ -393,11 +393,11 @@ fn decode_image(
if reader.format() != Some(ImageFormat::Png) {
return Err(bad_request(format!("{field} 文件必须是有效 PNG 文件")));
}
let decoded = reader
.decode()
let decoder = reader
.into_decoder()
.map_err(|error| map_decode_image_error(field, error))?;
if matches!(
decoded.color(),
decoder.color_type(),
image::ColorType::Rgba16
| image::ColorType::Rgb16
| image::ColorType::L16
@@ -407,7 +407,23 @@ fn decode_image(
"{field} 必须为 8-bit PNG(每通道 8 位)"
)));
}
let (width, height) = decoded.dimensions();
let (width, height) = decoder.dimensions();
let decoded_bytes = usize::try_from(decoder.total_bytes()).map_err(|_| {
bad_request(format!(
"{field} 文件超出 PNG 尺寸或解码资源上限(单边不超过 {RAW_IMAGE_MAX_EDGE}px"
))
})?;
let max_decoded_bytes =
usize::try_from(RAW_IMAGE_MAX_PIXELS.saturating_mul(4)).unwrap_or(usize::MAX);
if decoded_bytes > max_decoded_bytes {
return Err(bad_request(format!(
"{field} 文件超出 PNG 尺寸或解码资源上限(单边不超过 {RAW_IMAGE_MAX_EDGE}px"
)));
}
let mut decoded = vec![0_u8; decoded_bytes];
decoder
.read_image(&mut decoded)
.map_err(|error| map_decode_image_error(field, error))?;
Ok((
RawImageEditImage {
bytes,