[PATCH 2/6] Add memory reading functions

James Le Cuirot chewi at aura-online.co.uk
Sun Jun 1 15:28:37 PDT 2014


Taken and reworked from Aquaria. Most of the credit must be given to
"False Genesis" <false.genesis at googlemail.com> who originally wrote
this. I just refactored it, resulting in much less code duplication
and also ensured that the fixes for CVE-2010-1519 still hold.
---
 include/GL/glpng.h |   2 +
 src/glpng.c        | 190 +++++++++++++++++++++++++++++++++--------------------
 2 files changed, 122 insertions(+), 70 deletions(-)

diff --git a/include/GL/glpng.h b/include/GL/glpng.h
index d732d4d..172a757 100644
--- a/include/GL/glpng.h
+++ b/include/GL/glpng.h
@@ -93,9 +93,11 @@ extern int APIENTRY pngLoadRawF(FILE *file, pngRawInfo *rawinfo);
 
 extern int APIENTRY pngLoad(const char *filename, int mipmap, int trans, pngInfo *info);
 extern int APIENTRY pngLoadF(FILE *file, int mipmap, int trans, pngInfo *info);
+extern int APIENTRY pngLoadMem(const char *mem, int size, int mipmap, int trans, pngInfo *info);
 
 extern unsigned int APIENTRY pngBind(const char *filename, int mipmap, int trans, pngInfo *info, int wrapst, int minfilter, int magfilter);
 extern unsigned int APIENTRY pngBindF(FILE *file, int mipmap, int trans, pngInfo *info, int wrapst, int minfilter, int magfilter);
+extern unsigned int APIENTRY pngBindMem(const char *mem, int size, int mipmap, int trans, pngInfo *info, int wrapst, int minfilter, int magfilter);
 
 extern void APIENTRY pngSetStencil(unsigned char red, unsigned char green, unsigned char blue);
 extern void APIENTRY pngSetAlphaCallback(unsigned char (*callback)(unsigned char red, unsigned char green, unsigned char blue));
diff --git a/src/glpng.c b/src/glpng.c
index 688bbba..2e77638 100644
--- a/src/glpng.c
+++ b/src/glpng.c
@@ -32,6 +32,7 @@
 #include <GL/glext.h>
 #include <stdlib.h>
 #include <stdint.h>
+#include <string.h>
 #include <math.h>
 #include <png.h>
 
@@ -263,14 +264,13 @@ int APIENTRY pngLoadRawF(FILE *fp, pngRawInfo *pinfo) {
 	png_structp png;
 	png_infop   info;
 	png_infop   endinfo;
-	png_bytep   data = NULL;
-	png_bytep  *row_p = NULL;
 	double      fileGamma;
 
-	png_uint_32 width, height;
+	png_uint_32 width, height, i;
 	int depth, color;
 
-	png_uint_32 i;
+	png_bytep data = NULL;
+	png_bytep *row_p = NULL;
 
 	if (pinfo == NULL) return 0;
 
@@ -284,16 +284,8 @@ int APIENTRY pngLoadRawF(FILE *fp, pngRawInfo *pinfo) {
 	endinfo = png_create_info_struct(png);
 	if (!endinfo) return 0;
 
-	// DH: added following lines
 	if (setjmp(png_jmpbuf(png)))
-	{
-error:
-		png_destroy_read_struct(&png, &info, &endinfo);
-		free(data);
-		free(row_p);
-		return 0;
-	}
-	// ~DH
+		goto error;
 
 	png_init_io(png, fp);
 	png_set_sig_bytes(png, 8);
@@ -360,63 +352,29 @@ error:
 
 	pinfo->Data = data;
 
-   png_read_end(png, endinfo);
+	png_read_end(png, endinfo);
 	png_destroy_read_struct(&png, &info, &endinfo);
-
 	return 1;
-}
-
-int APIENTRY pngLoad(const char *filename, int mipmap, int trans, pngInfo *pinfo) {
-	int result;
-	FILE *fp = fopen(filename, "rb");
-	if (fp == NULL) return 0;
-
-	result = pngLoadF(fp, mipmap, trans, pinfo);
-
-	if (fclose(fp) != 0) return 0;
 
-	return result;
+error:
+	png_destroy_read_struct(&png, &info, &endinfo);
+	free(data);
+	free(row_p);
+	return 0;
 }
 
-int APIENTRY pngLoadF(FILE *fp, int mipmap, int trans, pngInfo *pinfo) {
+static int pngLoadCommon(int mipmap, int trans, pngInfo *pinfo, png_structp png, png_infop info, png_infop endinfo) {
 	GLint pack, unpack;
-	unsigned char header[8];
-	png_structp png;
-	png_infop   info;
-	png_infop   endinfo;
-	png_bytep   data = NULL;
-	png_bytep   data2 = NULL;
-	png_bytep  *row_p = NULL;
-	double      fileGamma;
+	double fileGamma;
 
-	png_uint_32 width, height, rw, rh;
+	png_uint_32 width, height, rw, rh, i;
 	int depth, color;
 
-	png_uint_32 i;
-
-	fread(header, 1, 8, fp);
-	if (!png_check_sig(header, 8)) return 0;
+	png_bytep data = NULL, data2 = NULL;
+	png_bytep *row_p = NULL;
 
-	png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
-	if (!png) return 0;
-	info = png_create_info_struct(png);
-	if (!info) return 0;
-	endinfo = png_create_info_struct(png);
-	if (!endinfo) return 0;
+	int ret = 0;
 
-	// DH: added following lines
-	if (setjmp(png_jmpbuf(png)))
-	{
-error:
-		png_destroy_read_struct(&png, &info, &endinfo);
-		free(data);
-		free(data2);
-		free(row_p);
-		return 0;
-	}
-	// ~DH
-
-	png_init_io(png, fp);
 	png_set_sig_bytes(png, 8);
 	png_read_info(png, info);
 	png_get_IHDR(png, info, &width, &height, &depth, &color, NULL, NULL, NULL);
@@ -473,12 +431,12 @@ error:
 	   height * png_get_rowbytes() may not be > PNG_UINT_32_MAX !
 	   This check fixes CVE-2010-1519. */
 	if ((uint64_t)height * png_get_rowbytes(png, info) > PNG_UINT_32_MAX)
-		goto error;
+		goto finish;
 
 	data = (png_bytep) malloc(png_get_rowbytes(png, info)*height);
 	row_p = (png_bytep *) malloc(sizeof(png_bytep)*height);
 	if (!data || !row_p)
-		goto error;
+		goto finish;
 
 	for (i = 0; i < height; i++) {
 		if (StandardOrientation)
@@ -498,11 +456,11 @@ error:
 
 		data2 = (png_bytep) malloc(rw*rh*channels);
 		if (!data2)
-			goto error;
+			goto finish;
 
  		/* Doesn't work on certain sizes */
 /* 		if (gluScaleImage(glformat, width, height, GL_UNSIGNED_BYTE, data, rw, rh, GL_UNSIGNED_BYTE, data2) != 0)
- 			return 0;
+			goto finish;
 */
 		Resize(channels, data, width, height, data2, rw, rh);
 
@@ -536,7 +494,7 @@ error:
 				case 1<<16: intf = GL_COLOR_INDEX16_EXT; break;
 				default:
 					/*printf("Warning: Colour depth %i not recognised\n", cols);*/
-					return 0;
+					goto finish;
 			}
 			glColorTableEXT(GL_TEXTURE_2D, GL_RGB8, cols, GL_RGB, GL_UNSIGNED_BYTE, pal);
 			glTexImage2D(GL_TEXTURE_2D, mipmap, intf, width, height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, data);
@@ -565,7 +523,7 @@ error:
 
 				default:
 					/*puts("glformat not set");*/
-					return 0;
+					goto finish;
 			}
 
 			if (mipmap == PNG_BUILDMIPMAPS)
@@ -583,7 +541,7 @@ error:
 			   original png had 3 channels and we are going to
 			   4 channels now! */
 			if ((uint64_t)width * height > (PNG_UINT_32_MAX >> 2))
-				goto error;
+				goto finish;
 
 			p = data, endp = p+width*height*3;
 			q = data2 = (png_bytep) malloc(sizeof(png_byte)*width*height*4);
@@ -681,19 +639,103 @@ error:
 			else
 				glTexImage2D(GL_TEXTURE_2D, mipmap, 4, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data2);
 
-			free(data2);
 		}
 
 		glPixelStorei(GL_PACK_ALIGNMENT, pack);
 		glPixelStorei(GL_UNPACK_ALIGNMENT, unpack);
 	} /* OpenGL end */
 
-   png_read_end(png, endinfo);
-	png_destroy_read_struct(&png, &info, &endinfo);
+	png_read_end(png, endinfo);
+	ret = 1;
 
+finish:
 	free(data);
+	free(data2);
+	free(row_p);
+	return ret;
+}
 
-	return 1;
+int APIENTRY pngLoad(const char *filename, int mipmap, int trans, pngInfo *pinfo) {
+	int result;
+	FILE *fp = fopen(filename, "rb");
+	if (fp == NULL) return 0;
+
+	result = pngLoadF(fp, mipmap, trans, pinfo);
+
+	if (fclose(fp) != 0) return 0;
+
+	return result;
+}
+
+int APIENTRY pngLoadF(FILE *fp, int mipmap, int trans, pngInfo *pinfo) {
+	unsigned char header[8];
+	png_structp png;
+	png_infop info;
+	png_infop endinfo;
+
+	int ret = 0;
+
+	fread(header, 1, 8, fp);
+	if (!png_check_sig(header, 8)) return 0;
+
+	png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+	if (!png) return 0;
+	info = png_create_info_struct(png);
+	if (!info) return 0;
+	endinfo = png_create_info_struct(png);
+	if (!endinfo) return 0;
+
+	if (!setjmp(png_jmpbuf(png))) {
+		png_init_io(png, fp);
+		ret = pngLoadCommon(mipmap, trans, pinfo, png, info, endinfo);
+	}
+
+	png_destroy_read_struct(&png, &info, &endinfo);
+	return ret;
+}
+
+typedef struct glpng_memread_struct {
+	png_bytep mem;
+	png_size_t rpos;
+} glpng_memread;
+
+static void glpng_read_mem(png_structp png, png_bytep dst, png_size_t size) {
+	glpng_memread *mr = (glpng_memread*) png_get_io_ptr(png);
+	memcpy(dst, mr->mem + mr->rpos, size);
+	mr->rpos += size;
+}
+
+int APIENTRY pngLoadMem(const char *mem, int size, int mipmap, int trans, pngInfo *pinfo) {
+	unsigned char header[8];
+	png_structp png;
+	png_infop info;
+	png_infop endinfo;
+	glpng_memread memread;
+
+	int ret = 0;
+
+	if (size < 8)
+		return 0; // error
+
+	memcpy(header, mem, 8);
+	if (!png_check_sig(header, 8)) return 0;
+
+	png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+	if (!png) return 0;
+	info = png_create_info_struct(png);
+	if (!info) return 0;
+	endinfo = png_create_info_struct(png);
+	if (!endinfo) return 0;
+
+	if (!setjmp(png_jmpbuf(png))) {
+		memread.rpos = 0;
+		memread.mem = ((png_bytep) mem) + 8;
+		png_set_read_fn(png, &memread, glpng_read_mem);
+		ret = pngLoadCommon(mipmap, trans, pinfo, png, info, endinfo);
+	}
+
+	png_destroy_read_struct(&png, &info, &endinfo);
+	return ret;
 }
 
 static unsigned int SetParams(int wrapst, int magfilter, int minfilter) {
@@ -727,6 +769,14 @@ unsigned int APIENTRY pngBindF(FILE *file, int mipmap, int trans, pngInfo *info,
 	return 0;
 }
 
+unsigned int APIENTRY pngBindMem(const char *mem, int size, int mipmap, int trans, pngInfo *info, int wrapst, int minfilter, int magfilter) {
+	unsigned int id = SetParams(wrapst, magfilter, minfilter);
+
+	if (id != 0 && pngLoadMem(mem, size, mipmap, trans, info))
+		return id;
+	return 0;
+}
+
 void APIENTRY pngSetStencil(unsigned char red, unsigned char green, unsigned char blue) {
 	StencilRed = red, StencilGreen = green, StencilBlue = blue;
 }
-- 
1.9.2



More information about the Games mailing list