From 20d840e6da15c1574b3ed998bc92f91d1e36c2a5 Mon Sep 17 00:00:00 2001
From: John Cupitt <jcupitt@gmail.com>
Date: Mon, 5 Mar 2018 14:42:09 +0000
Subject: [PATCH] fix a crash with delayed load

If a delayed load failed, it could leave the pipeline only half-set up.
Sebsequent threads could then segv.

Set a load-has-failed flag and test before generate.

See https://github.com/jcupitt/libvips/issues/893
---
 ChangeLog                      |  1 +
 libvips/foreign/foreign.c      | 25 +++++++++++++++++++------
 libvips/include/vips/foreign.h |  5 +++++
 3 files changed, 25 insertions(+), 6 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 68f646540..08aaab8c2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,6 @@
+12/2/18 started 8.6.3
+- fix a crash if a delayed load failed [gsharpsh00ter]
+
 8/12/16 started 8.4.5
 - allow libgsf-1.14.26 to help centos, thanks tdiprima
 
diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c
index 35ad2be52..fb03fd746 100644
--- a/libvips/foreign/foreign.c
+++ b/libvips/foreign/foreign.c
@@ -14,6 +14,8 @@
  * 	- forward progress signals from load
  * 23/5/16
  * 	- remove max-alpha stuff, this is now automatic
+ * 5/3/18
+ * 	- block _start if one start fails, see #893
  */
 
 /*
@@ -761,6 +763,11 @@ vips_foreign_load_start( VipsImage *out, void *a, void *b )
 	VipsForeignLoad *load = VIPS_FOREIGN_LOAD( b );
 	VipsForeignLoadClass *class = VIPS_FOREIGN_LOAD_GET_CLASS( load );
 
+	/* If this start has failed before in another thread, we can fail now.
+	 */
+	if( load->error )
+		return( NULL );
+
 	if( !load->real ) {
 		if( !(load->real = vips_foreign_load_temp( load )) )
 			return( NULL );
@@ -777,19 +784,25 @@ vips_foreign_load_start( VipsImage *out,
 		 */
 		load->real->progress_signal = load->out;
 
-		if( class->load( load ) ||
-			vips_image_pio_input( load->real ) ) 
-			return( NULL );
-
-		/* ->header() read the header into @out, load has read the
+		/* Load the image and check the result.
+		 *
+		 * ->header() read the header into @out, load has read the
 		 * image into @real. They must match exactly in size, bands,
 		 * format and coding for the copy to work.  
 		 *
 		 * Some versions of ImageMagick give different results between
 		 * Ping and Load for some formats, for example.
+		 *
+		 * If the load fails, we need to stop
 		 */
-		if( !vips_foreign_load_iscompat( load->real, out ) )
+		if( class->load( load ) ||
+			vips_image_pio_input( load->real ) || 
+			!vips_foreign_load_iscompat( load->real, out ) ) {
+			vips_operation_invalidate( VIPS_OPERATION( load ) ); 
+			load->error = TRUE;
+
 			return( NULL );
+		}
 
 		/* We have to tell vips that out depends on real. We've set
 		 * the demand hint below, but not given an input there.
diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h
index 47e285e37..dcbf21c82 100644
--- a/libvips/include/vips/foreign.h
+++ b/libvips/include/vips/foreign.h
@@ -149,6 +149,11 @@ typedef struct _VipsForeignLoad {
 	/* Set this to tag the operation as nocache.
 	 */
 	gboolean nocache;
+
+	/* Set if a start function fails. We want to prevent the other starts
+	 * from also triggering the load.
+	 */
+	gboolean error;
 } VipsForeignLoad;
 
 typedef struct _VipsForeignLoadClass {
