SDL_image: Factor out parts of IMG_LoadJPG where locals can be clobbered by longjmp (a5eac)

From a5eac02dacd8a8940ffccd1b8d0783c0b5f8ec7d Mon Sep 17 00:00:00 2001
From: Ozkan Sezer <[EMAIL REDACTED]>
Date: Sun, 27 Apr 2025 14:51:02 +0300
Subject: [PATCH] Factor out parts of IMG_LoadJPG where locals can be clobbered
 by longjmp

Also a minor tidy-up in IMG_LoadPNG()

( Reference issue: https://github.com/libsdl-org/SDL_image/issues/435 )
( Backport from SDL2 commit: 0dfa3d848c577481c4f97058267eda17b7f61aa8 )
---
 IMG_jpg.c | 134 +++++++++++++++++++++++++++++++-----------------------
 IMG_png.c |  34 ++++++++------
 2 files changed, 98 insertions(+), 70 deletions(-)

diff --git a/IMG_jpg.c b/IMG_jpg.c
index 9dd0e4c65..ecda75e90 100644
--- a/IMG_jpg.c
+++ b/IMG_jpg.c
@@ -371,53 +371,41 @@ static void output_no_message(j_common_ptr cinfo)
 	/* do nothing */
 }
 
-/* Load a JPEG type image from an SDL datasource */
-SDL_Surface *IMG_LoadJPG_RW(SDL_RWops *src)
-{
-	int start;
+struct loadjpeg_vars {
+	const char *error;
+	SDL_Surface *surface;
 	struct jpeg_decompress_struct cinfo;
-	JSAMPROW rowptr[1];
-	SDL_Surface *surface = NULL;
 	struct my_error_mgr jerr;
+};
 
-	if ( !src ) {
-		/* The error message has been set in SDL_RWFromFile */
-		return NULL;
-	}
-	start = SDL_RWtell(src);
-
-	if ( (IMG_Init(IMG_INIT_JPG) & IMG_INIT_JPG) == 0 ) {
-		return NULL;
-	}
+static SDL_bool LIBJPEG_LoadJPG_RW(SDL_RWops *src, struct loadjpeg_vars *vars)
+{
+	JSAMPROW rowptr[1];
 
 	/* Create a decompression structure and load the JPEG header */
-	cinfo.err = lib.jpeg_std_error(&jerr.errmgr);
-	jerr.errmgr.error_exit = my_error_exit;
-	jerr.errmgr.output_message = output_no_message;
-	if(setjmp(jerr.escape)) {
+	vars->cinfo.err = lib.jpeg_std_error(&vars->jerr.errmgr);
+	vars->jerr.errmgr.error_exit = my_error_exit;
+	vars->jerr.errmgr.output_message = output_no_message;
+	if (setjmp(vars->jerr.escape)) {
 		/* If we get here, libjpeg found an error */
-		lib.jpeg_destroy_decompress(&cinfo);
-		if ( surface != NULL ) {
-			SDL_FreeSurface(surface);
-		}
-		SDL_RWseek(src, start, RW_SEEK_SET);
-		IMG_SetError("JPEG loading error");
-		return NULL;
+		vars->error = "JPEG loading error";
+		lib.jpeg_destroy_decompress(&vars->cinfo);
+		return SDL_FALSE;
 	}
 
-	lib.jpeg_create_decompress(&cinfo);
-	jpeg_SDL_RW_src(&cinfo, src);
-	lib.jpeg_read_header(&cinfo, TRUE);
+	lib.jpeg_create_decompress(&vars->cinfo);
+	jpeg_SDL_RW_src(&vars->cinfo, src);
+	lib.jpeg_read_header(&vars->cinfo, TRUE);
 
-	if(cinfo.num_components == 4) {
+	if (vars->cinfo.num_components == 4) {
 		/* Set 32-bit Raw output */
-		cinfo.out_color_space = JCS_CMYK;
-		cinfo.quantize_colors = FALSE;
-		lib.jpeg_calc_output_dimensions(&cinfo);
+		vars->cinfo.out_color_space = JCS_CMYK;
+		vars->cinfo.quantize_colors = FALSE;
+		lib.jpeg_calc_output_dimensions(&vars->cinfo);
 
 		/* Allocate an output surface to hold the image */
-		surface = SDL_AllocSurface(SDL_SWSURFACE,
-		        cinfo.output_width, cinfo.output_height, 32,
+		vars->surface = SDL_AllocSurface(SDL_SWSURFACE,
+		                   vars->cinfo.output_width, vars->cinfo.output_height, 32,
 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
 		                   0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
 #else
@@ -425,19 +413,19 @@ SDL_Surface *IMG_LoadJPG_RW(SDL_RWops *src)
 #endif
 	} else {
 		/* Set 24-bit RGB output */
-		cinfo.out_color_space = JCS_RGB;
-		cinfo.quantize_colors = FALSE;
+		vars->cinfo.out_color_space = JCS_RGB;
+		vars->cinfo.quantize_colors = FALSE;
 #ifdef FAST_JPEG
-		cinfo.scale_num   = 1;
-		cinfo.scale_denom = 1;
-		cinfo.dct_method = JDCT_FASTEST;
-		cinfo.do_fancy_upsampling = FALSE;
+		vars->cinfo.scale_num   = 1;
+		vars->cinfo.scale_denom = 1;
+		vars->cinfo.dct_method = JDCT_FASTEST;
+		vars->cinfo.do_fancy_upsampling = FALSE;
 #endif
-		lib.jpeg_calc_output_dimensions(&cinfo);
+		lib.jpeg_calc_output_dimensions(&vars->cinfo);
 
 		/* Allocate an output surface to hold the image */
-		surface = SDL_AllocSurface(SDL_SWSURFACE,
-		        cinfo.output_width, cinfo.output_height, 24,
+		vars->surface = SDL_AllocSurface(SDL_SWSURFACE,
+		                   vars->cinfo.output_width, vars->cinfo.output_height, 24,
 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
 		                   0x0000FF, 0x00FF00, 0xFF0000,
 #else
@@ -446,24 +434,56 @@ SDL_Surface *IMG_LoadJPG_RW(SDL_RWops *src)
 		                   0);
 	}
 
-	if ( surface == NULL ) {
-		lib.jpeg_destroy_decompress(&cinfo);
-		SDL_RWseek(src, start, RW_SEEK_SET);
-		IMG_SetError("Out of memory");
-		return NULL;
+	if (vars->surface == NULL) {
+		vars-> error = "Out of memory";
+		lib.jpeg_destroy_decompress(&vars->cinfo);
+		return SDL_FALSE;
 	}
 
 	/* Decompress the image */
-	lib.jpeg_start_decompress(&cinfo);
-	while ( cinfo.output_scanline < cinfo.output_height ) {
-		rowptr[0] = (JSAMPROW)(Uint8 *)surface->pixels +
-		                    cinfo.output_scanline * surface->pitch;
-		lib.jpeg_read_scanlines(&cinfo, rowptr, (JDIMENSION) 1);
+	lib.jpeg_start_decompress(&vars->cinfo);
+	while (vars->cinfo.output_scanline < vars->cinfo.output_height) {
+		rowptr[0] = (JSAMPROW)(Uint8 *)vars->surface->pixels +
+				vars->cinfo.output_scanline * vars->surface->pitch;
+		lib.jpeg_read_scanlines(&vars->cinfo, rowptr, (JDIMENSION) 1);
+	}
+	lib.jpeg_finish_decompress(&vars->cinfo);
+	lib.jpeg_destroy_decompress(&vars->cinfo);
+
+	return SDL_TRUE;
+}
+
+/* Load a JPEG type image from an SDL datasource */
+SDL_Surface *IMG_LoadJPG_RW(SDL_RWops *src)
+{
+	int start;
+	struct loadjpeg_vars vars;
+
+	if (!src) {
+		/* The error message has been set in SDL_RWFromFile */
+		return NULL;
+	}
+
+	if ((IMG_Init(IMG_INIT_JPG) & IMG_INIT_JPG) == 0) {
+		return NULL;
+	}
+
+	start = SDL_RWtell(src);
+	memset(&vars, 0, sizeof(vars));
+
+	if (LIBJPEG_LoadJPG_RW(src, &vars)) {
+		return vars.surface;
+	}
+
+	SDL_RWseek(src, start, RW_SEEK_SET);
+	if (vars.surface) {
+		SDL_FreeSurface(vars.surface);
+	}
+	if (vars.error) {
+		IMG_SetError("%s", vars.error);
 	}
-	lib.jpeg_finish_decompress(&cinfo);
-	lib.jpeg_destroy_decompress(&cinfo);
 
-	return(surface);
+	return NULL;
 }
 
 #else
diff --git a/IMG_png.c b/IMG_png.c
index e757ac964..0618fff13 100644
--- a/IMG_png.c
+++ b/IMG_png.c
@@ -345,7 +345,7 @@ struct loadpng_vars {
 	png_bytep *row_pointers;
 };
 
-static void LIBPNG_LoadPNG_RW(SDL_RWops *src, struct loadpng_vars *vars)
+static SDL_bool LIBPNG_LoadPNG_RW(SDL_RWops *src, struct loadpng_vars *vars)
 {
 	png_uint_32 width, height;
 	int bit_depth, color_type, interlace_type, num_channels;
@@ -363,14 +363,14 @@ static void LIBPNG_LoadPNG_RW(SDL_RWops *src, struct loadpng_vars *vars)
 					  NULL,NULL,NULL);
 	if (vars->png_ptr == NULL) {
 		vars->error = "Couldn't allocate memory for PNG file or incompatible PNG dll";
-		return;
+		return SDL_FALSE;
 	}
 
 	 /* Allocate/initialize the memory for image information.  REQUIRED. */
 	vars->info_ptr = lib.png_create_info_struct(vars->png_ptr);
 	if (vars->info_ptr == NULL) {
 		vars->error = "Couldn't create image information for PNG file";
-		return;
+		return SDL_FALSE;
 	}
 
 	/* Set error handling if you are using setjmp/longjmp method (this is
@@ -384,7 +384,7 @@ static void LIBPNG_LoadPNG_RW(SDL_RWops *src, struct loadpng_vars *vars)
 #endif
 	{
 		vars->error = "Error reading the PNG file.";
-		return;
+		return SDL_FALSE;
 	}
 
 	/* Set up the input control */
@@ -471,7 +471,7 @@ static void LIBPNG_LoadPNG_RW(SDL_RWops *src, struct loadpng_vars *vars)
 				bit_depth*num_channels, Rmask,Gmask,Bmask,Amask);
 	if (vars->surface == NULL) {
 		vars->error = "Out of memory";
-		return;
+		return SDL_FALSE;
 	}
 
 	if (ckey != -1) {
@@ -488,7 +488,7 @@ static void LIBPNG_LoadPNG_RW(SDL_RWops *src, struct loadpng_vars *vars)
 	vars->row_pointers = (png_bytep*) malloc(sizeof(png_bytep)*height);
 	if (vars->row_pointers == NULL) {
 		vars->error = "Out of memory";
-		return;
+		return SDL_FALSE;
 	}
 	for (row = 0; row < (int)height; row++) {
 		vars->row_pointers[row] = (png_bytep)
@@ -528,12 +528,15 @@ static void LIBPNG_LoadPNG_RW(SDL_RWops *src, struct loadpng_vars *vars)
 		}
 	    }
 	}
+
+	return SDL_TRUE;
 }
 
 SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src)
 {
 	int start;
 	struct loadpng_vars vars;
+	SDL_bool success;
 
 	if ( !src ) {
 		/* The error message has been set in SDL_RWFromFile */
@@ -547,7 +550,7 @@ SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src)
 	start = SDL_RWtell(src);
 	memset(&vars, 0, sizeof(vars));
 
-	LIBPNG_LoadPNG_RW(src, &vars);
+	success = LIBPNG_LoadPNG_RW(src, &vars);
 
 	if (vars.png_ptr) {
 		lib.png_destroy_read_struct(&vars.png_ptr,
@@ -557,15 +560,20 @@ SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src)
 	if (vars.row_pointers) {
 		free(vars.row_pointers);
 	}
+	if (success) {
+		return vars.surface;
+	}
+
+	SDL_RWseek(src, start, RW_SEEK_SET);
+	if (vars.surface) {
+		SDL_FreeSurface(vars.surface);
+		vars.surface = NULL;
+	}
 	if (vars.error) {
-		SDL_RWseek(src, start, RW_SEEK_SET);
-		if (vars.surface) {
-			SDL_FreeSurface(vars.surface);
-			vars.surface = NULL;
-		}
 		IMG_SetError(vars.error);
 	}
-	return vars.surface;
+
+	return NULL;
 }
 
 #else