parameter and $ENV{HOME} not set.
- added VERSION numbers to most .pms
- convert t/t104ppm.t to Test::More
+- convert t/t107bmp.t to Test::More
+- convert t/t108tga.t to Test::More
+- error messages generated reading JPEG or PNG images are now
+ available via errstr()
=================================================================
if ( $input{'type'} eq 'jpeg' ) {
($self->{IMG},$self->{IPTCRAW}) = i_readjpeg_wiol( $IO );
if ( !defined($self->{IMG}) ) {
- $self->{ERRSTR}='unable to read jpeg image'; return undef;
+ $self->{ERRSTR}=$self->_error_as_msg(); return undef;
}
$self->{DEBUG} && print "loading a jpeg file\n";
return $self;
if ( $input{'type'} eq 'png' ) {
$self->{IMG}=i_readpng_wiol( $IO, -1 ); # Fixme, check if that length parameter is ever needed
if ( !defined($self->{IMG}) ) {
- $self->{ERRSTR}='unable to read png image';
+ $self->{ERRSTR} = $self->_error_as_msg();
return undef;
}
$self->{DEBUG} && print "loading a png file\n";
return $self;
}
+my @file_limit_names = qw/width height bytes/;
+
+sub set_file_limits {
+ shift;
+
+ my %opts = @_;
+ my %values;
+
+ if ($opts{reset}) {
+ @values{@file_limit_names} = (0) x @file_limit_names;
+ }
+ else {
+ @values{@file_limit_names} = i_get_image_file_limits();
+ }
+
+ for my $key (keys %values) {
+ defined $opts{$key} and $values{$key} = $opts{$key};
+ }
+
+ i_set_image_file_limits($values{width}, $values{height}, $values{bytes});
+}
+
+sub get_file_limits {
+ i_get_image_file_limits();
+}
+
# Shortcuts that can be exported
sub newcolor { Imager::Color->new(@_); }
getcolors() - L<Imager::ImageTypes> - get colors from the image
palette, if it has one
+get_file_limits() - L<Imager::Files/"Limiting the sizes of images you read">
+
getheight() - L<Imager::ImageTypes>
getpixel() - L<Imager::Draw/setpixel and getpixel>
setpixel() - L<Imager::Draw/setpixel and getpixel>
+set_file_limits() - L<Imager::Files/"Limiting the sizes of images you read">
+
string() - L<Imager::Font/string> - draw text on an image
tags() - L<Imager::ImageTypes> - fetch image tags
JPEG - L<Imager::Files/"JPEG">
+limiting image sizes - L<Imager::Files/"Limiting the sizes of images you read">
+
lines, drawing - L<Imager::Draw/line>
matrix - L<Imager::Matrix2d>,
i_rgb_to_hsvf(RETVAL);
OUTPUT:
RETVAL
-
MODULE = Imager PACKAGE = Imager::ImgRaw PREFIX = IIM_
myfree(data);
+undef_int
+i_set_image_file_limits(width, height, bytes)
+ int width
+ int height
+ int bytes
+
+void
+i_get_image_file_limits()
+ PREINIT:
+ int width, height, bytes;
+ PPCODE:
+ if (i_get_image_file_limits(&width, &height, &bytes)) {
+ EXTEND(SP, 3);
+ PUSHs(sv_2mortal(newSViv(width)));
+ PUSHs(sv_2mortal(newSViv(height)));
+ PUSHs(sv_2mortal(newSViv(bytes)));
+ }
+
MODULE = Imager PACKAGE = Imager::IO PREFIX = io_glue_
void
filters.o dynaload.o stackmach.o datatypes.o
regmach.o trans2.o quant.o error.o convert.o
map.o tags.o palimg.o maskimg.o img16.o rotate.o
- bmp.o tga.o rgb.o color.o fills.o imgdouble.o);
+ bmp.o tga.o rgb.o color.o fills.o imgdouble.o limits.o);
%opts=(
'NAME' => 'Imager',
- check llist_push() calls
- fix Imager::Color warning when $ENV{HOME} isn't set from
- _get_gimp_color()
+ _get_gimp_color() (done)
http://rt.cpan.org/NoAuth/Bug.html?id=13143
-- module version numbers
- http://rt.cpan.org/NoAuth/Bug.html?id=13047
+- module version numbers (done)
+ http://rt.cpan.org/NoAuth/Bug.html?id=13047
- allow limits to be set on the size of an image read from a file. This is
to prevent an attacker supplying huge images that consume all of
- memory causing a denial of service attack.
+ memory causing a denial of service attack. (done)
- implement gsamp()/gsampf()/plin() etc methods for those low level image
interfaces which don't yet have methods.
http://nntp.perl.org/group/perl.perl5.porters/102434
(#1521, #5608, #8231, #11429, #13058)
-- have $img->read() act like ($img) = Imager->read_multi()
+- have $img->read() act like ($img) = Imager->read_multi() on GIFs
- figure out what the nearest_color filter does, and document it
-#include "image.h"
#include <stdarg.h>
+#include "imagei.h"
/*
=head1 NAME
"clr_important %d\n", filesize, offbits, xsize, ysize, planes,
bit_count, compression, size_image, xres, yres, clr_used,
clr_important));
+
+ if (!i_int_check_image_file_limits(xsize, ysize, 3, sizeof(i_sample_t))) {
+ mm_log((1, "i_readbmp_wiol: image size exceeds limits\n"));
+ return NULL;
+ }
switch (bit_count) {
case 1:
-#include "image.h"
+#include "imagei.h"
#include <gif_lib.h>
#ifdef _MSCVER
#include <io.h>
cmapcnt++;
}
+ if (!i_int_check_image_file_limits(GifFile->SWidth, GifFile->SHeight, 3, sizeof(i_sample_t))) {
+ if (colour_table && *colour_table) {
+ myfree(*colour_table);
+ *colour_table = NULL;
+ }
+ DGifCloseFile(GifFile);
+ mm_log((1, "i_readgif: image size exceeds limits\n"));
+ return NULL;
+ }
im = i_img_empty_ch(NULL, GifFile->SWidth, GifFile->SHeight, 3);
if (!im) {
channels = 3;
if (got_gce && trans_index >= 0)
channels = 4;
+ if (!i_int_check_image_file_limits(Width, Height, channels, sizeof(i_sample_t))) {
+ free_images(results, *count);
+ mm_log((1, "i_readgif: image size exceeds limits\n"));
+ return NULL;
+ }
img = i_img_pal_new(Width, Height, channels, 256);
if (!img) {
free_images(results, *count);
i_color const *value);
extern void i_tags_print(i_img_tags *tags);
+/* image file limits */
+extern int
+i_set_image_file_limits(int width, int height, int bytes);
+extern int
+i_get_image_file_limits(int *width, int *height, int *bytes);
+
#endif
extern void i_get_combine(int combine, i_fill_combine_f *, i_fill_combinef_f *);
+extern int
+i_int_check_image_file_limits(int width, int height, int channels, int sample_size);
+
#include "ext.h"
extern UTIL_table_t i_UTIL_table;
#include <setjmp.h>
#include "iolayer.h"
-#include "image.h"
+#include "imagei.h"
#include "jpeglib.h"
#include "jerror.h"
#include <errno.h>
(void) jpeg_read_header(&cinfo, TRUE);
(void) jpeg_start_decompress(&cinfo);
+ if (!i_int_check_image_file_limits(cinfo.output_width, cinfo.output_height,
+ cinfo.output_components, sizeof(i_sample_t))) {
+ mm_log((1, "i_readjpeg: image size exceeds limits\n"));
+
+ jpeg_destroy_decompress(&cinfo);
+ return NULL;
+ }
im=i_img_empty_ch(NULL,cinfo.output_width,cinfo.output_height,cinfo.output_components);
if (!im) {
jpeg_destroy_decompress(&cinfo);
my @imgs = Imager->read_multi(file=>$filename)
or die "Cannot read: ", Imager->errstr;
+ Imager->set_file_limits(width=>$max_width, height=>$max_height)
+
=head1 DESCRIPTION
You can read and write a variety of images formats, assuming you have
# I'm writing jpegs to weird filenames
local $Imager::FORMATGUESS = sub { 'jpeg' };
+=head2 Limiting the sizes of images you read
+
+In some cases you will be receiving images from an untested source,
+such as submissions via CGI. To prevent such images from consuming
+large amounts of memory, you can set limits on the dimensions of
+images you read from files:
+
+=over
+
+=item *
+
+width - limit the width in pixels of the image
+
+=item *
+
+height - limit the height in pixels of the image
+
+=item *
+
+bytes - limits the amount of storage used by the image. This depends
+on the width, height, channels and sample size of the image. For
+paletted images this is calculated as if the image was expanded to a
+direct color image.
+
+=back
+
+To set the limits, call the class method set_file_limits:
+
+ Imager->set_file_limits(width=>$max_width, height=>$max_height);
+
+You can pass any or all of the limits above, any limits you do not
+pass are left as they were.
+
+Any limit of zero is treated as unlimited.
+
+By default, all of the limits are zero, or unlimited.
+
+You can reset all of the limited to their defaults by passing in the
+reset parameter as a true value:
+
+ # no limits
+ Imager->set_file_limits(reset=>1);
+
+This can be used with the other limits to reset all but the limit you
+pass:
+
+ # only width is limited
+ Imager->set_file_limits(reset=>1, width=>100);
+
+ # only bytes is limited
+ Imager->set_file_limits(reset=>1, bytes=>10_000_000);
+
+You can get the current limits with the get_file_limits() method:
+
+ my ($max_width, $max_height, $max_bytes) =
+ Imager->get_file_limits();
+
+
=head1 TYPE SPECIFIC INFORMATION
The different image formats can write different image type, and some have
--- /dev/null
+/*
+=head1 NAME
+
+limits.c - manages data/functions for limiting the sizes of images read from files.
+
+=head1 SYNOPSIS
+
+ // user code
+ if (!i_set_image_file_limits(max_width, max_height, max_bytes)) {
+ // error
+ }
+ i_get_image_file_limits(&max_width, &max_height, &max_bytes);
+
+ // file reader implementations
+ if (!i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))) {
+ // error handling
+ }
+
+=head1 DESCRIPTION
+
+Manage limits for image files read by Imager.
+
+Setting a value of zero means that limit will be ignored.
+
+ */
+
+#include "imagei.h"
+
+static int max_width, max_height;
+static int max_bytes;
+
+int
+i_set_image_file_limits(int width, int height, int bytes) {
+ i_clear_error();
+
+ if (width < 0) {
+ i_push_error(0, "width must be non-negative");
+ return 0;
+ }
+ if (height < 0) {
+ i_push_error(0, "height must be non-negative");
+ return 0;
+ }
+ if (bytes < 0) {
+ i_push_error(0, "bytes must be non-negative");
+ return 0;
+ }
+
+ max_width = width;
+ max_height = height;
+ max_bytes = bytes;
+
+ return 1;
+}
+
+int
+i_get_image_file_limits(int *width, int *height, int *bytes) {
+ i_clear_error();
+
+ *width = max_width;
+ *height = max_height;
+ *bytes = max_bytes;
+
+ return 1;
+}
+
+int
+i_int_check_image_file_limits(int width, int height, int channels, int sample_size) {
+ i_clear_error();
+
+ if (width <= 0) {
+ i_push_errorf(0, "file size limit - image width of %d is not positive",
+ width);
+ return 0;
+ }
+ if (max_width && width > max_width) {
+ i_push_errorf(0, "file size limit - image width of %d exceeds limit of %d",
+ width, max_width);
+ return 0;
+ }
+
+ if (height <= 0) {
+ i_push_errorf(0, "file size limit - image height %d is not positive",
+ height);
+ return 0;
+ }
+
+ if (max_height && height > max_height) {
+ i_push_errorf(0, "file size limit - image height of %d "
+ "exceeds limit of %d", height, max_height);
+ return 0;
+ }
+
+ if (channels < 1 || channels > MAXCHANNELS) {
+ i_push_errorf(0, "file size limit - channels %d out of range",
+ channels);
+ return 0;
+ }
+
+ if (sample_size < 1 || sample_size > sizeof(long double)) {
+ i_push_errorf(0, "file size limit - sample_size %d out of range",
+ sample_size);
+ return 0;
+ }
+
+ /* This overflow check is a bit more paranoid than usual.
+ We don't protect it under max_bytes since we always want to check
+ for overflow.
+ */
+ int bytes = width * height * channels * sample_size;
+ if (bytes / width != height * channels * sample_size
+ || bytes / height != width * channels * sample_size) {
+ i_push_error(0, "file size limit - integer overflow calculating storage");
+ return 0;
+ }
+ if (max_bytes) {
+ if (bytes > max_bytes) {
+ i_push_errorf(0, "file size limit - storage size of %d "
+ "exceeds limit of %d", bytes, max_bytes);
+ return 0;
+ }
+ }
+
+ return 1;
+}
#include "iolayer.h"
-#include "image.h"
+#include "imagei.h"
#include "png.h"
/* Check to see if a file is a PNG file using png_sig_cmp(). png_sig_cmp()
mm_log((1,"i_readpng_wiol: channels %d\n",channels));
+ if (!i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))) {
+ mm_log((1, "i_readpnm: image size exceeds limits\n"));
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+ return NULL;
+ }
+
png_set_strip_16(png_ptr);
png_set_packing(png_ptr);
if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr);
#include "image.h"
#include "log.h"
#include "iolayer.h"
+#include "imagei.h"
#include <stdlib.h>
#include <errno.h>
channels = (type == 3 || type == 6) ? 3:1;
pcount = width*height*channels;
+ if (!i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))) {
+ mm_log((1, "i_readpnm: image size exceeds limits\n"));
+ return NULL;
+ }
+
mm_log((1, "i_readpnm: (%d x %d), channels = %d, maxval = %d\n", width, height, channels, maxval));
im = i_img_empty_ch(NULL, width, height, channels);
use strict;
use lib 't';
-use Test::More tests => 3;
+use Test::More tests => 12;
use Imager;
Imager::init_log("testout/t1000files.log", 1);
is($out, '', "output should be empty");
}
+# test the file limit functions
+# by default the limits are zero (unlimited)
+print "# image file limits\n";
+is_deeply([ Imager->get_file_limits() ], [0, 0, 0],
+ "check defaults");
+ok(Imager->set_file_limits(width=>100), "set only width");
+is_deeply([ Imager->get_file_limits() ], [100, 0, 0 ],
+ "check width set");
+ok(Imager->set_file_limits(height=>150, bytes=>10000),
+ "set height and bytes");
+is_deeply([ Imager->get_file_limits() ], [ 100, 150, 10000 ],
+ "check all values now set");
+ok(Imager->set_file_limits(reset=>1, height => 99),
+ "set height and reset");
+is_deeply([ Imager->get_file_limits() ], [ 0, 99, 0 ],
+ "check only height is set");
+ok(Imager->set_file_limits(reset=>1),
+ "just reset");
+is_deeply([ Imager->get_file_limits() ], [ 0, 0, 0 ],
+ "check all are reset");
use strict;
use lib 't';
use Imager qw(:all);
-use Test::More tests => 9;
+use Test::More tests => 24;
init_log("testout/t101jpeg.log",1);
# check that the i_format tag is set
my @fmt = $imoo->tags(name=>'i_format');
is($fmt[0], 'jpeg', 'i_format tag');
+
+ { # check file limits are checked
+ my $limit_file = "testout/t101.jpg";
+ ok(Imager->set_file_limits(reset=>1, width=>149), "set width limit 149");
+ my $im = Imager->new;
+ ok(!$im->read(file=>$limit_file),
+ "should fail read due to size limits");
+ print "# ",$im->errstr,"\n";
+ like($im->errstr, qr/image width/, "check message");
+
+ ok(Imager->set_file_limits(reset=>1, height=>149), "set height limit 149");
+ ok(!$im->read(file=>$limit_file),
+ "should fail read due to size limits");
+ print "# ",$im->errstr,"\n";
+ like($im->errstr, qr/image height/, "check message");
+
+ ok(Imager->set_file_limits(reset=>1, width=>150), "set width limit 150");
+ ok($im->read(file=>$limit_file),
+ "should succeed - just inside width limit");
+ ok(Imager->set_file_limits(reset=>1, height=>150), "set height limit 150");
+ ok($im->read(file=>$limit_file),
+ "should succeed - just inside height limit");
+
+ # 150 x 150 x 3 channel image uses 67500 bytes
+ ok(Imager->set_file_limits(reset=>1, bytes=>67499),
+ "set bytes limit 67499");
+ ok(!$im->read(file=>$limit_file),
+ "should fail - too many bytes");
+ print "# ",$im->errstr,"\n";
+ like($im->errstr, qr/storage size/, "check error message");
+ ok(Imager->set_file_limits(reset=>1, bytes=>67500),
+ "set bytes limit 67500");
+ ok($im->read(file=>$limit_file),
+ "should succeed - just inside bytes limit");
+ Imager->set_file_limits(reset=>1);
+ }
}
#!perl -w
use strict;
use lib 't';
-use Test::More tests => 13;
+use Test::More tests => 28;
# Before `make install' is performed this script should be runnable with
# `make test'. After `make install' it should work as `perl test.pl'
# upgrade libpng
EOS
}
+
+ { # check file limits are checked
+ my $limit_file = "testout/t102.png";
+ ok(Imager->set_file_limits(reset=>1, width=>149), "set width limit 149");
+ my $im = Imager->new;
+ ok(!$im->read(file=>$limit_file),
+ "should fail read due to size limits");
+ print "# ",$im->errstr,"\n";
+ like($im->errstr, qr/image width/, "check message");
+
+ ok(Imager->set_file_limits(reset=>1, height=>149), "set height limit 149");
+ ok(!$im->read(file=>$limit_file),
+ "should fail read due to size limits");
+ print "# ",$im->errstr,"\n";
+ like($im->errstr, qr/image height/, "check message");
+
+ ok(Imager->set_file_limits(reset=>1, width=>150), "set width limit 150");
+ ok($im->read(file=>$limit_file),
+ "should succeed - just inside width limit");
+ ok(Imager->set_file_limits(reset=>1, height=>150), "set height limit 150");
+ ok($im->read(file=>$limit_file),
+ "should succeed - just inside height limit");
+
+ # 150 x 150 x 3 channel image uses 67500 bytes
+ ok(Imager->set_file_limits(reset=>1, bytes=>67499),
+ "set bytes limit 67499");
+ ok(!$im->read(file=>$limit_file),
+ "should fail - too many bytes");
+ print "# ",$im->errstr,"\n";
+ like($im->errstr, qr/storage size/, "check error message");
+ ok(Imager->set_file_limits(reset=>1, bytes=>67500),
+ "set bytes limit 67500");
+ ok($im->read(file=>$limit_file),
+ "should succeed - just inside bytes limit");
+ Imager->set_file_limits(reset=>1);
+ }
+
}
#!perl -w
use Imager ':all';
-use Test::More tests => 45;
-BEGIN { require "t/testtools.pl"; }
+use lib 't';
+use Test::More tests => 60;
use strict;
-print "1..45\n";
-
init_log("testout/t104ppm.log",1);
my $green = i_color_new(0,255,0,255);
is($type, 'pnm', "check i_format");
}
+{ # check file limits are checked
+ my $limit_file = "testout/t104.ppm";
+ ok(Imager->set_file_limits(reset=>1, width=>149), "set width limit 149");
+ my $im = Imager->new;
+ ok(!$im->read(file=>$limit_file),
+ "should fail read due to size limits");
+ print "# ",$im->errstr,"\n";
+ like($im->errstr, qr/image width/, "check message");
+
+ ok(Imager->set_file_limits(reset=>1, height=>149), "set height limit 149");
+ ok(!$im->read(file=>$limit_file),
+ "should fail read due to size limits");
+ print "# ",$im->errstr,"\n";
+ like($im->errstr, qr/image height/, "check message");
+
+ ok(Imager->set_file_limits(reset=>1, width=>150), "set width limit 150");
+ ok($im->read(file=>$limit_file),
+ "should succeed - just inside width limit");
+ ok(Imager->set_file_limits(reset=>1, height=>150), "set height limit 150");
+ ok($im->read(file=>$limit_file),
+ "should succeed - just inside height limit");
+
+ # 150 x 150 x 3 channel image uses 67500 bytes
+ ok(Imager->set_file_limits(reset=>1, bytes=>67499),
+ "set bytes limit 67499");
+ ok(!$im->read(file=>$limit_file),
+ "should fail - too many bytes");
+ print "# ",$im->errstr,"\n";
+ like($im->errstr, qr/storage size/, "check error message");
+ ok(Imager->set_file_limits(reset=>1, bytes=>67500),
+ "set bytes limit 67500");
+ ok($im->read(file=>$limit_file),
+ "should succeed - just inside bytes limit");
+ Imager->set_file_limits(reset=>1);
+}
+
sub openimage {
my $fname = shift;
local(*FH);
use strict;
$|=1;
use lib 't';
-use Test::More tests => 69;
+use Test::More tests => 84;
use Imager qw(:all);
BEGIN { require "t/testtools.pl"; }
use Carp 'confess';
my ($type) = $im->tags(name=>'i_format');
is($type, 'gif', 'check i_format for single image read');
}
+
+ { # check file limits are checked
+ my $limit_file = "testout/t105.gif";
+ ok(Imager->set_file_limits(reset=>1, width=>149), "set width limit 149");
+ my $im = Imager->new;
+ ok(!$im->read(file=>$limit_file),
+ "should fail read due to size limits");
+ print "# ",$im->errstr,"\n";
+ like($im->errstr, qr/image width/, "check message");
+
+ ok(Imager->set_file_limits(reset=>1, height=>149), "set height limit 149");
+ ok(!$im->read(file=>$limit_file),
+ "should fail read due to size limits");
+ print "# ",$im->errstr,"\n";
+ like($im->errstr, qr/image height/, "check message");
+
+ ok(Imager->set_file_limits(reset=>1, width=>150), "set width limit 150");
+ ok($im->read(file=>$limit_file),
+ "should succeed - just inside width limit");
+ ok(Imager->set_file_limits(reset=>1, height=>150), "set height limit 150");
+ ok($im->read(file=>$limit_file),
+ "should succeed - just inside height limit");
+
+ # 150 x 150 x 3 channel image uses 67500 bytes
+ ok(Imager->set_file_limits(reset=>1, bytes=>67499),
+ "set bytes limit 67499");
+ ok(!$im->read(file=>$limit_file),
+ "should fail - too many bytes");
+ print "# ",$im->errstr,"\n";
+ like($im->errstr, qr/storage size/, "check error message");
+ ok(Imager->set_file_limits(reset=>1, bytes=>67500),
+ "set bytes limit 67500");
+ ok($im->read(file=>$limit_file),
+ "should succeed - just inside bytes limit");
+ Imager->set_file_limits(reset=>1);
+ }
}
sub test_readgif_cb {
#!perl -w
-print "1..74\n";
-use Imager qw(:all);
use strict;
+use lib 't';
+use Test::More tests => 89;
+use Imager qw(:all);
init_log("testout/t107bmp.log",1);
-BEGIN { require 't/testtools.pl'; } # BEGIN to apply prototypes
+#BEGIN { require 't/testtools.pl'; } # BEGIN to apply prototypes
my $base_diff = 0;
# if you change this make sure you generate new compressed versions
Imager::i_tags_add($img, 'i_xres', 0, '300', 0);
Imager::i_tags_add($img, 'i_yres', 0, undef, 300);
-write_test(1, $img, "testout/t107_24bit.bmp");
+write_test($img, "testout/t107_24bit.bmp");
# 'webmap' is noticably faster than the default
my $im8 = Imager::i_img_to_pal($img, { make_colors=>'webmap',
translate=>'errdiff'});
-write_test(2, $im8, "testout/t107_8bit.bmp");
+write_test($im8, "testout/t107_8bit.bmp");
# use a fixed palette so we get reproducible results for the compressed
# version
my @pal16 = map { NC($_) }
qw(605844 966600 0148b2 00f800 bf0a33 5e009e
2ead1b 0000f8 004b01 fd0000 0e1695 000002);
my $im4 = Imager::i_img_to_pal($img, { colors=>\@pal16, make_colors=>'none' });
-write_test(3, $im4, "testout/t107_4bit.bmp");
+write_test($im4, "testout/t107_4bit.bmp");
my $im1 = Imager::i_img_to_pal($img, { colors=>[ NC(0, 0, 0), NC(176, 160, 144) ],
make_colors=>'none', translate=>'errdiff' });
-write_test(4, $im1, "testout/t107_1bit.bmp");
+write_test($im1, "testout/t107_1bit.bmp");
my $bi_rgb = 0;
my $bi_rle8 = 1;
my $bi_rle4 = 2;
my $bi_bitfields = 3;
-read_test(5, "testout/t107_24bit.bmp", $img,
+read_test("testout/t107_24bit.bmp", $img,
bmp_compression=>0, bmp_bit_count => 24);
-read_test(6, "testout/t107_8bit.bmp", $im8,
+read_test("testout/t107_8bit.bmp", $im8,
bmp_compression=>0, bmp_bit_count => 8);
-read_test(7, "testout/t107_4bit.bmp", $im4,
+read_test("testout/t107_4bit.bmp", $im4,
bmp_compression=>0, bmp_bit_count => 4);
-read_test(8, "testout/t107_1bit.bmp", $im1, bmp_compression=>0,
+read_test("testout/t107_1bit.bmp", $im1, bmp_compression=>0,
bmp_bit_count=>1);
# the following might have slight differences
$base_diff = i_img_diff($img, $im8) * 2;
print "# base difference $base_diff\n";
-read_test(9, "testimg/comp4.bmp", $im4,
+read_test("testimg/comp4.bmp", $im4,
bmp_compression=>$bi_rle4, bmp_bit_count => 4);
-read_test(10, "testimg/comp8.bmp", $im8,
+read_test("testimg/comp8.bmp", $im8,
bmp_compression=>$bi_rle8, bmp_bit_count => 8);
my $imoo = Imager->new;
-if ($imoo->read(file=>'testout/t107_24bit.bmp')) {
- print "ok 11\n";
-}
-else {
- print "not ok 11 # ",$imoo->errstr,"\n";
-}
-if ($imoo->write(file=>'testout/t107_oo.bmp')) {
- print "ok 12\n";
-}
-else {
- print "not 12 # ",$imoo->errstr,"\n";
-}
+# read via OO
+ok($imoo->read(file=>'testout/t107_24bit.bmp'), "read via OO")
+ or print "# ",$imoo->errstr,"\n";
+
+ok($imoo->write(file=>'testout/t107_oo.bmp'), "write via OO")
+ or print "# ",$imoo->errstr,"\n";
# various invalid format tests
# we have so many different test images to try to detect all the possible
# failure paths in the code, adding these did detect real problems
print "# catch various types of invalid bmp files\n";
-my $test_num = 13;
my @tests =
(
# entries in each array ref are:
'out of range palette size (1-bit)' ],
[ 'badcomp1.bmp', 'unknown 1-bit BMP compression (1)',
'invalid compression value (1-bit)' ],
- [ 'bad1wid0.bmp', 'Image sizes must be positive',
+ [ 'bad1wid0.bmp', 'file size limit - image width of 0 is not positive',
'width 0 (1-bit)' ],
- [ 'bad4oflow.bmp', 'integer overflow calculating image allocation',
+ [ 'bad4oflow.bmp',
+ 'file size limit - integer overflow calculating storage',
'overflow integers on 32-bit machines (1-bit)', '32bitonly' ],
[ 'short1.bmp', 'failed reading 1-bit bmp data',
'short 1-bit' ],
'short uncompressed 4-bit' ],
[ 'short4rle.bmp', 'missing data during decompression',
'short compressed 4-bit' ],
- [ 'bad4wid0.bmp', 'Image sizes must be positive',
+ [ 'bad4wid0.bmp', 'file size limit - image width of 0 is not positive',
'width 0 (4-bit)' ],
- [ 'bad4widbig.bmp', 'Image sizes must be positive',
+ [ 'bad4widbig.bmp', 'file size limit - image width of -2147483628 is not positive',
'width big (4-bit)' ],
- [ 'bad4oflow.bmp', 'integer overflow calculating image allocation',
+ [ 'bad4oflow.bmp', 'file size limit - integer overflow calculating storage',
'overflow integers on 32-bit machines (4-bit)', '32bitonly' ],
# 8-bit/pixel BMPs
'short uncompressed 8-bit' ],
[ 'short8rle.bmp', 'missing data during decompression',
'short compressed 8-bit' ],
- [ 'bad8wid0.bmp', 'Image sizes must be positive',
+ [ 'bad8wid0.bmp', 'file size limit - image width of 0 is not positive',
'width 0 (8-bit)' ],
- [ 'bad8oflow.bmp', 'integer overflow calculating image allocation',
+ [ 'bad8oflow.bmp', 'file size limit - integer overflow calculating storage',
'overflow integers on 32-bit machines (8-bit)', '32bitonly' ],
# 24-bit/pixel BMPs
[ 'short24.bmp', 'failed reading image data',
'short 24-bit' ],
- [ 'bad24wid0.bmp', 'Image sizes must be positive',
+ [ 'bad24wid0.bmp', 'file size limit - image width of 0 is not positive',
'width 0 (24-bit)' ],
- [ 'bad24oflow.bmp', 'integer overflow calculating image allocation',
+ [ 'bad24oflow.bmp', 'file size limit - integer overflow calculating storage',
'overflow integers on 32-bit machines (24-bit)', '32bitonly' ],
[ 'bad24comp.bmp', 'unknown 24-bit BMP compression (4)',
'bad compression (24-bit)' ],
my $intsize = $Config{intsize};
for my $test (@tests) {
my ($file, $error, $comment, $bit32only) = @$test;
- if (!$bit32only || $intsize == 4) {
- okn($test_num++, !$imoo->read(file=>"testimg/$file"), $comment);
- isn($test_num++, $imoo->errstr, $error, "check error message");
- }
- else {
- skipn($test_num, 2, "only tested on 32-bit machines");
- $test_num += 2;
+ SKIP:
+ {
+ skip("only tested on 32-bit machines", 2)
+ if $bit32only && $intsize != 4;
+ ok(!$imoo->read(file=>"testimg/$file"), $comment);
+ is($imoo->errstr, $error, "check error message");
}
}
my $base_im = Imager->new;
my $got_base =
- okn($test_num++, $base_im->read(file=>"testimg/$base_file"),
+ ok($base_im->read(file=>"testimg/$base_file"),
"read original")
or print "# ",$base_im->errstr,"\n";
my $off_im = Imager->new;
my $got_off =
- okn($test_num++, $off_im->read(file=>"testimg/$off_file"),
+ ok($off_im->read(file=>"testimg/$off_file"),
"read offset file")
or print "# ",$off_im->errstr,"\n";
- if ($got_base && $got_off) {
- okn($test_num++, !i_img_diff($base_im->{IMG}, $off_im->{IMG}),
+ SKIP:
+ {
+ skip("missed one file", 1)
+ unless $got_base && $got_off;
+ is(i_img_diff($base_im->{IMG}, $off_im->{IMG}), 0,
"compare base and offset image ($bits bits)");
}
- else {
- skipn($test_num++, 1, "missed one file");
- }
+}
+
+{ # check file limits are checked
+ my $limit_file = "testout/t104.ppm";
+ ok(Imager->set_file_limits(reset=>1, width=>149), "set width limit 149");
+ my $im = Imager->new;
+ ok(!$im->read(file=>$limit_file),
+ "should fail read due to size limits");
+ print "# ",$im->errstr,"\n";
+ like($im->errstr, qr/image width/, "check message");
+
+ ok(Imager->set_file_limits(reset=>1, height=>149), "set height limit 149");
+ ok(!$im->read(file=>$limit_file),
+ "should fail read due to size limits");
+ print "# ",$im->errstr,"\n";
+ like($im->errstr, qr/image height/, "check message");
+
+ ok(Imager->set_file_limits(reset=>1, width=>150), "set width limit 150");
+ ok($im->read(file=>$limit_file),
+ "should succeed - just inside width limit");
+ ok(Imager->set_file_limits(reset=>1, height=>150), "set height limit 150");
+ ok($im->read(file=>$limit_file),
+ "should succeed - just inside height limit");
+
+ # 150 x 150 x 3 channel image uses 67500 bytes
+ ok(Imager->set_file_limits(reset=>1, bytes=>67499),
+ "set bytes limit 67499");
+ ok(!$im->read(file=>$limit_file),
+ "should fail - too many bytes");
+ print "# ",$im->errstr,"\n";
+ like($im->errstr, qr/storage size/, "check error message");
+ ok(Imager->set_file_limits(reset=>1, bytes=>67500),
+ "set bytes limit 67500");
+ ok($im->read(file=>$limit_file),
+ "should succeed - just inside bytes limit");
+ Imager->set_file_limits(reset=>1);
}
sub write_test {
- my ($test_num, $im, $filename) = @_;
+ my ($im, $filename) = @_;
local *FH;
if (open FH, "> $filename") {
binmode FH;
my $IO = Imager::io_new_fd(fileno(FH));
- if (Imager::i_writebmp_wiol($im, $IO)) {
- print "ok $test_num\n";
- }
- else {
- print "not ok $test_num # ",Imager->_error_as_msg(),"\n";
+ unless (ok(Imager::i_writebmp_wiol($im, $IO), $filename)) {
+ print "# ",Imager->_error_as_msg(),"\n";
}
undef $IO;
close FH;
}
else {
- print "not ok $test_num # $!\n";
+ fail("could not open $filename: $!");
}
}
sub read_test {
- my ($test_num, $filename, $im, %tags) = @_;
+ my ($filename, $im, %tags) = @_;
local *FH;
print "# read_test: $filename\n";
if ($im_read) {
my $diff = i_img_diff($im, $im_read);
if ($diff > $base_diff) {
- print "not ok $test_num # image mismatch $diff\n";
+ fail("image mismatch reading $filename");
}
else {
my $tags_ok = 1;
}
}
}
- if ($tags_ok) {
- print "ok $test_num\n";
- }
- else {
- print "not ok $test_num # bad tag values\n";
- }
+ ok($tags_ok, "reading $filename");
# for my $i (0 .. Imager::i_tags_count($im_read)-1) {
# my ($name, $value) = Imager::i_tags_get($im_read, $i);
# print "# tag '$name' => '$value'\n";
}
}
else {
- print "not ok $test_num # ",Imager->_error_as_msg(),"\n";
+ fail("could not read $filename: ".Imager->_error_as_msg());
}
undef $IO;
close FH;
}
else {
- print "not ok $test_num # $!\n";
+ fail("could not open $filename: $!");
}
}
#!perl -w
-print "1..21\n";
use Imager qw(:all);
use strict;
+use Test::More tests=>36;
BEGIN { require "t/testtools.pl"; }
init_log("testout/t108tga.log",1);
my $img = create_test_image();
my $base_diff = 0;
-write_test(1, $img, "testout/t108_24bit.tga", 0, 0, "");
-write_test(2, $img, "testout/t108_24bit_rle.tga", 0, 1, "");
-write_test(3, $img, "testout/t108_15bit.tga", 1, 1, "");
-write_test(4, $img, "testout/t108_15bit_rle.tga", 1, 1, "");
+write_test($img, "testout/t108_24bit.tga", 0, 0, "");
+write_test($img, "testout/t108_24bit_rle.tga", 0, 1, "");
+write_test($img, "testout/t108_15bit.tga", 1, 1, "");
+write_test($img, "testout/t108_15bit_rle.tga", 1, 1, "");
# 'webmap' is noticably faster than the default
my $im8 = Imager::i_img_to_pal($img, { make_colors=>'webmap',
translate=>'errdiff'});
-write_test(5, $im8, "testout/t108_8bit.tga", 0, 0, "");
-write_test(6, $im8, "testout/t108_8bit_rle.tga", 0, 1, "");
-write_test(7, $im8, "testout/t108_8_15bit.tga", 1, 0, "");
-write_test(8, $im8, "testout/t108_8_15bit_rle.tga", 1, 1, "");
+write_test($im8, "testout/t108_8bit.tga", 0, 0, "");
+write_test($im8, "testout/t108_8bit_rle.tga", 0, 1, "");
+write_test($im8, "testout/t108_8_15bit.tga", 1, 0, "");
+write_test($im8, "testout/t108_8_15bit_rle.tga", 1, 1, "");
# use a fixed palette so we get reproducible results for the compressed
make_colors=>'none',
translate=>'errdiff' });
-write_test(9, $im4, "testout/t108_4bit.tga", 0, 1, "");
-write_test(10, $im1, "testout/t108_1bit.tga", 0, 1, "This is a comment!");
+write_test($im4, "testout/t108_4bit.tga", 0, 1, "");
+write_test($im1, "testout/t108_1bit.tga", 0, 1, "This is a comment!");
-read_test(11, "testout/t108_24bit.tga", $img);
-read_test(12, "testout/t108_8bit.tga", $im8);
-read_test(13, "testout/t108_4bit.tga", $im4);
-read_test(14, "testout/t108_1bit.tga", $im1);
+read_test("testout/t108_24bit.tga", $img);
+read_test("testout/t108_8bit.tga", $im8);
+read_test("testout/t108_4bit.tga", $im4);
+read_test("testout/t108_1bit.tga", $im1);
# the following might have slight differences
print "# base difference $base_diff\n";
my $imoo = Imager->new;
-if ($imoo->read(file=>'testout/t108_24bit.tga')) {
- print "ok 15\n";
-} else {
- print "not ok 15 # ",$imoo->errstr,"\n";
-}
+ok($imoo->read(file=>'testout/t108_24bit.tga'),
+ "OO read image")
+ or print "# ",$imoo->errstr,"\n";
-if ($imoo->write(file=>'testout/t108_oo.tga')) {
- print "ok 16\n";
-} else {
- print "not ok 16 # ",$imoo->errstr,"\n";
-}
+ok($imoo->write(file=>'testout/t108_oo.tga'),
+ "OO write image")
+ or print "# ",$imoo->errstr,"\n";
my ($type) = $imoo->tags(name=>'i_format');
-isn(17, $type, 'tga', "check i_format tag");
+is($type, 'tga', "check i_format tag");
# in 0.44 and earlier, reading an image with an idstring of 128 or more
# bytes would result in an allocation error, if the platform char type
# was signed
$imoo = Imager->new;
-okn(18, $imoo->read(file=>'testimg/longid.tga'), "read long id image");
+ok($imoo->read(file=>'testimg/longid.tga'), "read long id image");
my ($id) = $imoo->tags(name=>'tga_idstring');
-isn(19, $id, "X" x 128, "check tga_idstring tag");
+is($id, "X" x 128, "check tga_idstring tag");
my ($bitspp) = $imoo->tags(name=>'tga_bitspp');
-isn(20, $bitspp, 24, "check tga_bitspp tag");
+is($bitspp, 24, "check tga_bitspp tag");
my ($compressed) = $imoo->tags(name=>'compressed');
-isn(21, $compressed, 1, "check compressed tag");
+is($compressed, 1, "check compressed tag");
+
+{ # check file limits are checked
+ my $limit_file = "testout/t108_24bit.tga";
+ ok(Imager->set_file_limits(reset=>1, width=>149), "set width limit 149");
+ my $im = Imager->new;
+ ok(!$im->read(file=>$limit_file),
+ "should fail read due to size limits");
+ print "# ",$im->errstr,"\n";
+ like($im->errstr, qr/image width/, "check message");
+
+ ok(Imager->set_file_limits(reset=>1, height=>149), "set height limit 149");
+ ok(!$im->read(file=>$limit_file),
+ "should fail read due to size limits");
+ print "# ",$im->errstr,"\n";
+ like($im->errstr, qr/image height/, "check message");
+
+ ok(Imager->set_file_limits(reset=>1, width=>150), "set width limit 150");
+ ok($im->read(file=>$limit_file),
+ "should succeed - just inside width limit");
+ ok(Imager->set_file_limits(reset=>1, height=>150), "set height limit 150");
+ ok($im->read(file=>$limit_file),
+ "should succeed - just inside height limit");
+
+ # 150 x 150 x 3 channel image uses 67500 bytes
+ ok(Imager->set_file_limits(reset=>1, bytes=>67499),
+ "set bytes limit 67499");
+ ok(!$im->read(file=>$limit_file),
+ "should fail - too many bytes");
+ print "# ",$im->errstr,"\n";
+ like($im->errstr, qr/storage size/, "check error message");
+ ok(Imager->set_file_limits(reset=>1, bytes=>67500),
+ "set bytes limit 67500");
+ ok($im->read(file=>$limit_file),
+ "should succeed - just inside bytes limit");
+ Imager->set_file_limits(reset=>1);
+}
sub write_test {
- my ($test_num, $im, $filename, $wierdpack, $compress, $idstring) = @_;
+ my ($im, $filename, $wierdpack, $compress, $idstring) = @_;
local *FH;
if (open FH, "> $filename") {
binmode FH;
my $IO = Imager::io_new_fd(fileno(FH));
- if (Imager::i_writetga_wiol($im, $IO, $wierdpack, $compress, $idstring)) {
- print "ok $test_num\n";
- } else {
- print "not ok $test_num # ",Imager->_error_as_msg(),"\n";
- }
+ ok(Imager::i_writetga_wiol($im, $IO, $wierdpack, $compress, $idstring),
+ "write $filename")
+ or print "# ",Imager->_error_as_msg(),"\n";
undef $IO;
close FH;
} else {
- print "not ok $test_num # $!\n";
+ fail("write $filename: open failed: $!");
}
}
sub read_test {
- my ($test_num, $filename, $im, %tags) = @_;
+ my ($filename, $im, %tags) = @_;
local *FH;
if (open FH, "< $filename") {
my $im_read = Imager::i_readtga_wiol($IO,-1);
if ($im_read) {
my $diff = i_img_diff($im, $im_read);
- if ($diff > $base_diff) {
- print "not ok $test_num # image mismatch $diff\n";
- }
- print "ok $test_num\n";
+ cmp_ok($diff, '<=', $base_diff,
+ "check read image vs original");
} else {
- print "not ok $test_num # ",Imager->_error_as_msg(),"\n";
+ fail("read $filename ".Imager->_error_as_msg());
}
undef $IO;
close FH;
} else {
- print "not ok $test_num # $!\n";
+ fail("read $filename, open failure: $!");
}
}
-#include "image.h"
+#include "imagei.h"
#include "log.h"
#include "iolayer.h"
width = header.width;
height = header.height;
+
/* Set tags here */
channels = 1;
break;
}
+
+ if (!i_int_check_image_file_limits(width, height, channels,
+ sizeof(i_sample_t))) {
+ mm_log((1, "i_readtga_wiol: image size exceeds limits\n"));
+ return NULL;
+ }
img = mapped ?
i_img_pal_new(width, height, channels, 256) :