Skip to content

Commit 7a9a459

Browse files
authored
Fixed crash when transcoding one- or two-channel KTX2 textures (#12629)
# Objective Fixes a crash when transcoding one- or two-channel KTX2 textures ## Solution transcoded array has been pre-allocated up to levels.len using a macros. Rgb8 transcoding already uses that and addresses transcoded array by an index. R8UnormSrgb and Rg8UnormSrgb were pushing on top of the transcoded vec, resulting in first levels.len() vectors to stay empty, and second levels.len() levels actually being transcoded, which then resulted in out of bounds read when copying levels to gpu
1 parent a362c27 commit 7a9a459

File tree

1 file changed

+45
-16
lines changed
  • crates/bevy_render/src/texture

1 file changed

+45
-16
lines changed

crates/bevy_render/src/texture/ktx2.rs

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,12 @@ pub fn ktx2_buffer_to_image(
9090
TranscodeFormat::R8UnormSrgb => {
9191
let (mut original_width, mut original_height) = (width, height);
9292

93-
for level_data in &levels {
94-
transcoded.push(
95-
level_data
96-
.iter()
97-
.copied()
98-
.map(|v| (Srgba::gamma_function(v as f32 / 255.) * 255.).floor() as u8)
99-
.collect::<Vec<u8>>(),
100-
);
93+
for (level, level_data) in levels.iter().enumerate() {
94+
transcoded[level] = level_data
95+
.iter()
96+
.copied()
97+
.map(|v| (Srgba::gamma_function(v as f32 / 255.) * 255.).floor() as u8)
98+
.collect::<Vec<u8>>();
10199

102100
// Next mip dimensions are half the current, minimum 1x1
103101
original_width = (original_width / 2).max(1);
@@ -109,14 +107,12 @@ pub fn ktx2_buffer_to_image(
109107
TranscodeFormat::Rg8UnormSrgb => {
110108
let (mut original_width, mut original_height) = (width, height);
111109

112-
for level_data in &levels {
113-
transcoded.push(
114-
level_data
115-
.iter()
116-
.copied()
117-
.map(|v| (Srgba::gamma_function(v as f32 / 255.) * 255.).floor() as u8)
118-
.collect::<Vec<u8>>(),
119-
);
110+
for (level, level_data) in levels.iter().enumerate() {
111+
transcoded[level] = level_data
112+
.iter()
113+
.copied()
114+
.map(|v| (Srgba::gamma_function(v as f32 / 255.) * 255.).floor() as u8)
115+
.collect::<Vec<u8>>();
120116

121117
// Next mip dimensions are half the current, minimum 1x1
122118
original_width = (original_width / 2).max(1);
@@ -1493,3 +1489,36 @@ pub fn ktx2_format_to_texture_format(
14931489
}
14941490
})
14951491
}
1492+
1493+
#[cfg(test)]
1494+
mod tests {
1495+
use crate::texture::CompressedImageFormats;
1496+
1497+
use super::ktx2_buffer_to_image;
1498+
1499+
#[test]
1500+
fn test_ktx_levels() {
1501+
// R8UnormSrgb textture with 4x4 pixels data and 3 levels of mipmaps
1502+
let buffer = vec![
1503+
0xab, 0x4b, 0x54, 0x58, 0x20, 0x32, 0x30, 0xbb, 0x0d, 10, 0x1a, 10, 0x0f, 0, 0, 0, 1,
1504+
0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0,
1505+
0, 0, 0x98, 0, 0, 0, 0x2c, 0, 0, 0, 0xc4, 0, 0, 0, 0x5c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1506+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0x28, 1, 0, 0, 0, 0, 0, 0, 0x10, 0, 0, 0, 0, 0, 0, 0, 0x10,
1507+
0, 0, 0, 0, 0, 0, 0, 0x24, 1, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0,
1508+
0, 0, 0, 0x20, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
1509+
0x2c, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0x28, 0, 1, 1, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
1510+
0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0, 0, 0, 0x12, 0, 0, 0, 0x4b, 0x54, 0x58,
1511+
0x6f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0, 0x72, 0x64, 0, 0,
1512+
0, 0x10, 0, 0, 0, 0x4b, 0x54, 0x58, 0x73, 0x77, 0x69, 0x7a, 0x7a, 0x6c, 0x65, 0, 0x72,
1513+
0x72, 0x72, 0x31, 0, 0x2c, 0, 0, 0, 0x4b, 0x54, 0x58, 0x77, 0x72, 0x69, 0x74, 0x65,
1514+
0x72, 0, 0x74, 0x6f, 0x6b, 0x74, 0x78, 0x20, 0x76, 0x34, 0x2e, 0x33, 0x2e, 0x30, 0x7e,
1515+
0x32, 0x38, 0x20, 0x2f, 0x20, 0x6c, 0x69, 0x62, 0x6b, 0x74, 0x78, 0x20, 0x76, 0x34,
1516+
0x2e, 0x33, 0x2e, 0x30, 0x7e, 0x31, 0, 0x4a, 0, 0, 0, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
1517+
0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
1518+
0x4a,
1519+
];
1520+
let supported_compressed_formats = CompressedImageFormats::empty();
1521+
let result = ktx2_buffer_to_image(&buffer, supported_compressed_formats, true);
1522+
assert!(result.is_ok());
1523+
}
1524+
}

0 commit comments

Comments
 (0)