wuffs: protect against crafted images that cause overflows
Fixes #9579 Protect against panics caused by integer overflows by using functions that allow integer overflows to be caught instead of causing a panic. Also protect against DOS from images that might not cause an overflow but do consume an absurd amount of memory by limiting images to a maximum size of 4GiB (which is the maximum size of `image-storage-limit`).pull/9581/head
parent
0f64b9a8e8
commit
ec55cbc879
|
|
@ -2,7 +2,7 @@ const std = @import("std");
|
|||
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const Error = std.mem.Allocator.Error || error{WuffsError};
|
||||
pub const Error = std.mem.Allocator.Error || error{ WuffsError, Overflow };
|
||||
|
||||
pub fn check(log: anytype, status: *const c.struct_wuffs_base__status__struct) error{WuffsError}!void {
|
||||
if (!c.wuffs_base__status__is_ok(status)) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ const c = @import("c.zig").c;
|
|||
const Error = @import("error.zig").Error;
|
||||
const check = @import("error.zig").check;
|
||||
const ImageData = @import("main.zig").ImageData;
|
||||
const maximum_image_size = @import("main.zig").maximum_image_size;
|
||||
const mul = std.math.mul;
|
||||
|
||||
const log = std.log.scoped(.wuffs_jpeg);
|
||||
|
||||
|
|
@ -61,9 +63,20 @@ pub fn decode(alloc: Allocator, data: []const u8) Error!ImageData {
|
|||
height,
|
||||
);
|
||||
|
||||
const size: usize = try mul(
|
||||
usize,
|
||||
try mul(usize, width, height),
|
||||
@sizeOf(c.wuffs_base__color_u32_argb_premul),
|
||||
);
|
||||
|
||||
if (size > maximum_image_size) {
|
||||
log.warn("image size {d} is larger than the maximum allowed ({d})", .{ size, maximum_image_size });
|
||||
return error.Overflow;
|
||||
}
|
||||
|
||||
const destination = try alloc.alloc(
|
||||
u8,
|
||||
width * height * @sizeOf(c.wuffs_base__color_u32_argb_premul),
|
||||
size,
|
||||
);
|
||||
errdefer alloc.free(destination);
|
||||
|
||||
|
|
@ -131,3 +144,8 @@ test "jpeg_decode_FFFFFF" {
|
|||
try std.testing.expectEqual(1, data.height);
|
||||
try std.testing.expectEqualSlices(u8, &.{ 255, 255, 255, 255 }, data.data);
|
||||
}
|
||||
|
||||
test "jpeg: too big" {
|
||||
const data = decode(std.testing.allocator, @embedFile("too_big.jpg"));
|
||||
try std.testing.expectError(error.Overflow, data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ pub const jpeg = @import("jpeg.zig");
|
|||
pub const swizzle = @import("swizzle.zig");
|
||||
pub const Error = @import("error.zig").Error;
|
||||
|
||||
/// The maximum image size, based on the 4G limit of Ghostty's
|
||||
/// `image-storage-limit` config.
|
||||
pub const maximum_image_size = 4 * 1024 * 1024 * 1024;
|
||||
|
||||
pub const ImageData = struct {
|
||||
width: u32,
|
||||
height: u32,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ const c = @import("c.zig").c;
|
|||
const Error = @import("error.zig").Error;
|
||||
const check = @import("error.zig").check;
|
||||
const ImageData = @import("main.zig").ImageData;
|
||||
const maximum_image_size = @import("main.zig").maximum_image_size;
|
||||
const mul = std.math.mul;
|
||||
|
||||
const log = std.log.scoped(.wuffs_png);
|
||||
|
||||
|
|
@ -61,9 +63,20 @@ pub fn decode(alloc: Allocator, data: []const u8) Error!ImageData {
|
|||
height,
|
||||
);
|
||||
|
||||
const size: usize = try mul(
|
||||
usize,
|
||||
try mul(usize, width, height),
|
||||
@sizeOf(c.wuffs_base__color_u32_argb_premul),
|
||||
);
|
||||
|
||||
if (size > maximum_image_size) {
|
||||
log.warn("image size {d} is larger than the maximum allowed ({d})", .{ size, maximum_image_size });
|
||||
return error.Overflow;
|
||||
}
|
||||
|
||||
const destination = try alloc.alloc(
|
||||
u8,
|
||||
width * height * @sizeOf(c.wuffs_base__color_u32_argb_premul),
|
||||
size,
|
||||
);
|
||||
errdefer alloc.free(destination);
|
||||
|
||||
|
|
@ -131,3 +144,8 @@ test "png_decode_FFFFFF" {
|
|||
try std.testing.expectEqual(1, data.height);
|
||||
try std.testing.expectEqualSlices(u8, &.{ 255, 255, 255, 255 }, data.data);
|
||||
}
|
||||
|
||||
test "png: too big" {
|
||||
const data = decode(std.testing.allocator, @embedFile("too_big.png"));
|
||||
try std.testing.expectError(error.Overflow, data);
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 12 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1021 KiB |
|
|
@ -433,6 +433,7 @@ pub const LoadingImage = struct {
|
|||
) catch |err| switch (err) {
|
||||
error.WuffsError => return error.InvalidData,
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
error.Overflow => return error.InvalidData,
|
||||
};
|
||||
defer alloc.free(result.data);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue