diff --git a/libvips/conversion/cache.c b/libvips/conversion/cache.c index e91d6123..52f31790 100644 --- a/libvips/conversion/cache.c +++ b/libvips/conversion/cache.c @@ -159,7 +159,10 @@ vips_cache_init( VipsCache *cache ) * documentation for that operation for details. * * It uses a set of background threads to calculate pixels and the various - * active cache operations coordinate so as not to overwhelm your system. + * active cache operations coordinate so as not to overwhelm your system. When + * a request is made for an area of pixels, the operation will block until all + * of those pixels have been calculated. Pixels are calculated with a set of + * threads. * * See also: vips_tilecache(). * diff --git a/libvips/conversion/copy.c b/libvips/conversion/copy.c index d70660b7..f927d849 100644 --- a/libvips/conversion/copy.c +++ b/libvips/conversion/copy.c @@ -200,7 +200,8 @@ vips_copy_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) if( vips_region_prepare( ir, r ) ) return( -1 ); - if( copy->swap && swap ) { + if( copy->swap && + swap ) { int y; for( y = 0; y < r->height; y++ ) { @@ -239,9 +240,12 @@ static const char *vips_copy_names[] = { static int vips_copy_build( VipsObject *object ) { + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); VipsConversion *conversion = VIPS_CONVERSION( object ); VipsCopy *copy = (VipsCopy *) object; + guint64 image_size_before; + guint64 image_size_after; int i; if( VIPS_OBJECT_CLASS( vips_copy_parent_class )->build( object ) ) @@ -254,6 +258,11 @@ vips_copy_build( VipsObject *object ) VIPS_DEMAND_STYLE_THINSTRIP, copy->in, NULL ) ) return( -1 ); + /* We try to stop the worst crashes by at least ensuring that we don't + * increase the number of pixels which might be addressed. + */ + image_size_before = VIPS_IMAGE_SIZEOF_IMAGE( conversion->out ); + /* Use props to adjust header fields. */ for( i = 0; i < VIPS_NUMBER( vips_copy_names ); i++ ) { @@ -291,6 +300,13 @@ vips_copy_build( VipsObject *object ) } } + image_size_after = VIPS_IMAGE_SIZEOF_IMAGE( conversion->out ); + if( image_size_after > image_size_before ) { + vips_error( class->nickname, + "%s", _( "image size too large" ) ); + return( -1 ); + } + if( vips_image_generate( conversion->out, vips_start_one, vips_copy_gen, vips_stop_one, copy->in, copy ) ) @@ -439,7 +455,8 @@ vips_copy_init( VipsCopy *copy ) * You can optionally set any or all header fields during the copy. Some * header fields, such as "xres", the horizontal resolution, are safe to * change in any way, others, such as "width" will cause immediate crashes if - * they are not set carefully. + * they are not set carefully. The operation will block changes which make the + * image size grow, see VIPS_IMAGE_SIZEOF_IMAGE(). * * Setting @swap to %TRUE will make vips_copy() swap the byte ordering of * pixels according to the image's format. diff --git a/python/test_conversion.py b/python/test_conversion.py index a044a7e8..866f51d6 100755 --- a/python/test_conversion.py +++ b/python/test_conversion.py @@ -147,5 +147,36 @@ class TestConversion(unittest.TestCase): self.run_binary(self.all_images, bandrank, fmt = noncomplex_formats) + def test_cache(self): + def cache(x): + if isinstance(x, Vips.Image): + return x.cache() + else: + return x + + self.run_unary(self.all_images, cache) + + def test_copy(self): + x = self.colour.copy(interpretation = Vips.Interpretation.LAB) + self.assertEqual(x.interpretation, Vips.Interpretation.LAB) + x = self.colour.copy(xres = 42) + self.assertEqual(x.xres, 42) + x = self.colour.copy(yres = 42) + self.assertEqual(x.yres, 42) + x = self.colour.copy(xoffset = 42) + self.assertEqual(x.xoffset, 42) + x = self.colour.copy(yoffset = 42) + self.assertEqual(x.yoffset, 42) + x = self.colour.copy(bands = 1) + self.assertEqual(x.bands, 1) + x = self.colour.copy(format = Vips.BandFormat.USHORT, bands = 1) + self.assertEqual(x.format, Vips.BandFormat.USHORT) + x = self.colour.copy(coding = Vips.Coding.NONE) + self.assertEqual(x.coding, Vips.Coding.NONE) + x = self.colour.copy(width = 42) + self.assertEqual(x.width, 42) + x = self.colour.copy(height = 42) + self.assertEqual(x.height, 42) + if __name__ == '__main__': unittest.main()