mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 08:46:08 +03:00
terminal/kitty-gfx: zlib decompression for data validation
This commit is contained in:
@ -243,7 +243,7 @@ fn loadAndAddImage(
|
||||
}
|
||||
|
||||
// Validate and store our image
|
||||
try img.validate();
|
||||
try img.validate(alloc);
|
||||
try storage.addImage(alloc, img);
|
||||
return img;
|
||||
}
|
||||
@ -255,6 +255,7 @@ fn encodeError(r: *Response, err: EncodeableError) void {
|
||||
switch (err) {
|
||||
error.OutOfMemory => r.message = "ENOMEM: out of memory",
|
||||
error.InvalidData => r.message = "EINVAL: invalid data",
|
||||
error.DecompressionFailed => r.message = "EINVAL: decompression failed",
|
||||
error.UnsupportedFormat => r.message = "EINVAL: unsupported format",
|
||||
error.UnsupportedMedium => r.message = "EINVAL: unsupported medium",
|
||||
error.DimensionsRequired => r.message = "EINVAL: dimensions required",
|
||||
|
@ -5,6 +5,8 @@ const ArenaAllocator = std.heap.ArenaAllocator;
|
||||
|
||||
const command = @import("graphics_command.zig");
|
||||
|
||||
const log = std.log.scoped(.kitty_gfx);
|
||||
|
||||
/// Maximum width or height of an image. Taken directly from Kitty.
|
||||
const max_dimension = 10000;
|
||||
|
||||
@ -57,20 +59,53 @@ pub const Image = struct {
|
||||
width: u32 = 0,
|
||||
height: u32 = 0,
|
||||
format: Format = .rgb,
|
||||
compression: command.Transmission.Compression = .none,
|
||||
data: []const u8 = "",
|
||||
|
||||
pub const Format = enum { rgb, rgba };
|
||||
|
||||
pub const Error = error{
|
||||
InvalidData,
|
||||
DecompressionFailed,
|
||||
DimensionsRequired,
|
||||
DimensionsTooLarge,
|
||||
UnsupportedFormat,
|
||||
UnsupportedMedium,
|
||||
};
|
||||
|
||||
/// The length of the data in bytes, uncompressed. While this will
|
||||
/// decompress compressed data to count the bytes it doesn't actually
|
||||
/// store the decompressed data so this doesn't allocate much.
|
||||
pub fn dataLen(self: *const Image, alloc: Allocator) !usize {
|
||||
return switch (self.compression) {
|
||||
.none => self.data.len,
|
||||
.zlib_deflate => zlib: {
|
||||
var fbs = std.io.fixedBufferStream(self.data);
|
||||
|
||||
var stream = std.compress.zlib.decompressStream(alloc, fbs.reader()) catch |err| {
|
||||
log.warn("zlib decompression failed: {}", .{err});
|
||||
return error.DecompressionFailed;
|
||||
};
|
||||
defer stream.deinit();
|
||||
|
||||
var counting_stream = std.io.countingReader(stream.reader());
|
||||
const counting_reader = counting_stream.reader();
|
||||
|
||||
var buf: [4096]u8 = undefined;
|
||||
while (counting_reader.readAll(&buf)) |_| {} else |err| {
|
||||
if (err != error.EndOfStream) {
|
||||
log.warn("zlib decompression failed: {}", .{err});
|
||||
return error.DecompressionFailed;
|
||||
}
|
||||
}
|
||||
|
||||
break :zlib counting_stream.bytes_read;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// Validate that the image appears valid.
|
||||
pub fn validate(self: *const Image) !void {
|
||||
pub fn validate(self: *const Image, alloc: Allocator) !void {
|
||||
const bpp: u32 = switch (self.format) {
|
||||
.rgb => 3,
|
||||
.rgba => 4,
|
||||
@ -86,11 +121,12 @@ pub const Image = struct {
|
||||
// applications fail because the test that Kitty documents itself
|
||||
// uses an invalid value.
|
||||
const expected_len = self.width * self.height * bpp;
|
||||
const actual_len = try self.dataLen(alloc);
|
||||
std.log.warn(
|
||||
"width={} height={} bpp={} expected_len={} actual_len={}",
|
||||
.{ self.width, self.height, bpp, expected_len, self.data.len },
|
||||
.{ self.width, self.height, bpp, expected_len, actual_len },
|
||||
);
|
||||
if (self.data.len < expected_len) return error.InvalidData;
|
||||
if (actual_len < expected_len) return error.InvalidData;
|
||||
}
|
||||
|
||||
/// Load an image from a transmission. The data in the command will be
|
||||
@ -135,6 +171,7 @@ pub const Image = struct {
|
||||
.number = t.image_number,
|
||||
.width = t.width,
|
||||
.height = t.height,
|
||||
.compression = t.compression,
|
||||
.format = switch (t.format) {
|
||||
.rgb => .rgb,
|
||||
.rgba => .rgba,
|
||||
|
Reference in New Issue
Block a user