diff --git a/ANNOUNCE b/ANNOUNCE index eb7867d4a..904124621 100644 --- a/ANNOUNCE +++ b/ANNOUNCE @@ -60,6 +60,21 @@ Version 1.6.0beta02 [December 21, 2011] in libpng 1.5.7. Version 1.6.0beta03 [December 21, 2011] + Start-up code size improvements, error handler flexibility. These changes + alter how the tricky allocation of the initial png_struct and png_info + structures are handled. png_info is now handled in pretty much the same + way as everything else, except that the allocations handle NULL return + silently. png_struct is changed in a similar way on allocation and on + deallocation a 'safety' error handler is put in place (which should never + be required). The error handler itself is changed to permit mismatches + in the application and libpng error buffer size; however, this means a + silent change to the API to return the jmp_buf if the size doesn't match + the size from the libpng compilation; libpng now allocates the memory and + this may fail. Overall these changes result in slight code size + reductions; however, this is a reduction in code that is always executed + so is particularly valuable. Overall on a 64-bit system the libpng DLL + decreases in code size by 1733 bytes. pngerror.o increases in size by + about 465 bytes because of the new functionality. Send comments/corrections/commendations to png-mng-implement at lists.sf.net (subscription required; visit diff --git a/CHANGES b/CHANGES index c5295e2b1..c4a6b9d7e 100644 --- a/CHANGES +++ b/CHANGES @@ -3811,6 +3811,21 @@ Version 1.6.0beta02 [December 21, 2011] in libpng 1.5.7. Version 1.6.0beta03 [December 21, 2011] + Start-up code size improvements, error handler flexibility. These changes + alter how the tricky allocation of the initial png_struct and png_info + structures are handled. png_info is now handled in pretty much the same + way as everything else, except that the allocations handle NULL return + silently. png_struct is changed in a similar way on allocation and on + deallocation a 'safety' error handler is put in place (which should never + be required). The error handler itself is changed to permit mismatches + in the application and libpng error buffer size; however, this means a + silent change to the API to return the jmp_buf if the size doesn't match + the size from the libpng compilation; libpng now allocates the memory and + this may fail. Overall these changes result in slight code size + reductions; however, this is a reduction in code that is always executed + so is particularly valuable. Overall on a 64-bit system the libpng DLL + decreases in code size by 1733 bytes. pngerror.o increases in size by + about 465 bytes because of the new functionality. Send comments/corrections/commendations to png-mng-implement at lists.sf.net (subscription required; visit diff --git a/png.c b/png.c index 1be8dd8d5..b1fb74a2b 100644 --- a/png.c +++ b/png.c @@ -72,26 +72,20 @@ png_sig_cmp(png_const_bytep sig, png_size_t start, png_size_t num_to_check) PNG_FUNCTION(voidpf /* PRIVATE */, png_zalloc,(voidpf png_ptr, uInt items, uInt size),PNG_ALLOCATED) { - png_voidp ptr; - png_structp p=(png_structp)png_ptr; - png_uint_32 save_flags=p->flags; - png_alloc_size_t num_bytes; + png_alloc_size_t num_bytes = size; if (png_ptr == NULL) - return (NULL); + return NULL; - if (items > PNG_UINT_32_MAX/size) + if (items >= (~(png_alloc_size_t)0)/size) { - png_warning (p, "Potential overflow in png_zalloc()"); - return (NULL); + png_warning (png_voidcast(png_structp, png_ptr), + "Potential overflow in png_zalloc()"); + return NULL; } - num_bytes = (png_alloc_size_t)items * size; - p->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK; - ptr = (png_voidp)png_malloc((png_structp)png_ptr, num_bytes); - p->flags=save_flags; - - return ((voidpf)ptr); + num_bytes *= items; + return png_malloc_warn(png_voidcast(png_structp, png_ptr), num_bytes); } /* Function to free memory for zlib */ @@ -220,12 +214,124 @@ png_user_version_check(png_structp png_ptr, png_const_charp user_png_ver) return 1; } -/* Allocate the memory for an info_struct for the application. We don't - * really need the png_ptr, but it could potentially be useful in the - * future. This should be used in favour of malloc(png_sizeof(png_info)) - * and png_info_init() so that applications that want to use a shared - * libpng don't have to be recompiled if png_info changes size. +/* Generic function to create a png_struct for either read or write - this + * contains the common initialization. */ +PNG_FUNCTION(png_structp /* PRIVATE */, +png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED) +{ + png_struct create_struct; +# ifdef PNG_SETJMP_SUPPORTED + jmp_buf create_jmp_buf; +# endif + + /* This temporary stack-allocated structure is used to provide a place to + * build enough context to allow the user provided memory allocator (if any) + * to be called. + */ + png_memset(&create_struct, 0, sizeof create_struct); + + /* Added at libpng-1.2.6 */ +# ifdef PNG_USER_LIMITS_SUPPORTED + create_struct.user_width_max = PNG_USER_WIDTH_MAX; + create_struct.user_height_max = PNG_USER_HEIGHT_MAX; + +# ifdef PNG_USER_CHUNK_CACHE_MAX + /* Added at libpng-1.2.43 and 1.4.0 */ + create_struct.user_chunk_cache_max = PNG_USER_CHUNK_CACHE_MAX; +# endif + +# ifdef PNG_SET_USER_CHUNK_MALLOC_MAX + /* Added at libpng-1.2.43 and 1.4.1, required only for read but exists + * in png_struct regardless. + */ + create_struct.user_chunk_malloc_max = PNG_USER_CHUNK_MALLOC_MAX; +# endif +# endif + + /* The following two API calls simply set fields in png_struct, so it is safe + * to do them now even though error handling is not yet set up. + */ +# ifdef PNG_USER_MEM_SUPPORTED + png_set_mem_fn(&create_struct, mem_ptr, malloc_fn, free_fn); +# endif + + /* (*error_fn) can return control to the caller after the error_ptr is set, + * this will result in a memory leak unless the error_fn does something + * extremely sophisticated. The design lacks merit but is implicit in the + * API. + */ + png_set_error_fn(&create_struct, error_ptr, error_fn, warn_fn); + +# ifdef PNG_SETJMP_SUPPORTED + if (!setjmp(create_jmp_buf)) + { + /* Temporarily fake out the longjmp information until we have + * successfully completed this function. This only works if we have + * setjmp() support compiled in, but it is safe - this stuff should + * never happen. + */ + create_struct.jmp_buf_ptr = &create_jmp_buf; + create_struct.jmp_buf_size = 0; /*stack allocation*/ + create_struct.longjmp_fn = longjmp; +# else + { +# endif + /* Call the general version checker (shared with read and write code): + */ + if (png_user_version_check(&create_struct, user_png_ver)) + { + + /* TODO: delay initializing the zlib structure until it really is + * needed. + */ + /* Initialize zbuf - compression/decompression buffer */ + create_struct.zbuf_size = PNG_ZBUF_SIZE; + create_struct.zbuf = png_voidcast(png_bytep, + png_malloc_warn(&create_struct, create_struct.zbuf_size)); + + /* Finally allocate the png_struct itself. */ + if (create_struct.zbuf != NULL) + { + png_structp png_ptr = png_voidcast(png_structp, + png_malloc_warn(&create_struct, sizeof *png_ptr)); + + if (png_ptr != NULL) + { +# ifdef PNG_SETJMP_SUPPORTED + /* Eliminate the local error handling: */ + create_struct.jmp_buf_ptr = NULL; + create_struct.jmp_buf_size = 0; + create_struct.longjmp_fn = 0; +# endif + + *png_ptr = create_struct; + + /* This is the successful return point */ + return png_ptr; + } + } + } + } + + /* A longjmp because of a bug in the application storage allocator or a + * simple failure to allocate the png_struct. + */ + if (create_struct.zbuf != NULL) + { + png_bytep zbuf = create_struct.zbuf; + + /* Ensure we don't keep on returning to this point: */ + create_struct.zbuf = NULL; + png_free(&create_struct, zbuf); + } + + return NULL; +} + +/* Allocate the memory for an info_struct for the application. */ PNG_FUNCTION(png_infop,PNGAPI png_create_info_struct,(png_structp png_ptr),PNG_ALLOCATED) { @@ -234,24 +340,29 @@ png_create_info_struct,(png_structp png_ptr),PNG_ALLOCATED) png_debug(1, "in png_create_info_struct"); if (png_ptr == NULL) - return (NULL); + return NULL; + + /* Use the internal API that does not (or at least should not) error out, so + * that this call always returns ok. The application typically sets up the + * error handling *after* creating the info_struct because this is the way it + * has always been done in 'example.c'. + */ + info_ptr = png_voidcast(png_infop, png_malloc_base(png_ptr, + sizeof *info_ptr)); -#ifdef PNG_USER_MEM_SUPPORTED - info_ptr = (png_infop)png_create_struct_2(PNG_STRUCT_INFO, - png_ptr->malloc_fn, png_ptr->mem_ptr); -#else - info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO); -#endif if (info_ptr != NULL) - png_info_init_3(&info_ptr, png_sizeof(png_info)); + png_memset(info_ptr, 0, sizeof *info_ptr); - return (info_ptr); + return info_ptr; } /* This function frees the memory associated with a single info struct. * Normally, one would use either png_destroy_read_struct() or * png_destroy_write_struct() to free an info struct, but this may be - * useful for some applications. + * useful for some applications. From libpng 1.6.0 this function is also used + * internally to implement the png_info release part of the 'struct' destroy + * APIs. This ensures that all possible approaches free the same data (all of + * it). */ void PNGAPI png_destroy_info_struct(png_structp png_ptr, png_infopp info_ptr_ptr) @@ -268,25 +379,31 @@ png_destroy_info_struct(png_structp png_ptr, png_infopp info_ptr_ptr) if (info_ptr != NULL) { - png_info_destroy(png_ptr, info_ptr); - -#ifdef PNG_USER_MEM_SUPPORTED - png_destroy_struct_2((png_voidp)info_ptr, png_ptr->free_fn, - png_ptr->mem_ptr); -#else - png_destroy_struct((png_voidp)info_ptr); -#endif + /* Do this first in case of an error below; if the app implements its own + * memory management this can lead to png_free calling png_error, which + * will abort this routine and return control to the app error handler. + * An infinite loop may result if it then tries to free the same info + * ptr. + */ *info_ptr_ptr = NULL; + + png_info_destroy(png_ptr, info_ptr); + png_free(png_ptr, info_ptr); } } /* Initialize the info structure. This is now an internal function (0.89) * and applications using it are urged to use png_create_info_struct() - * instead. + * instead. Use deprecated in 1.6.0, internal use removed (used internally it + * is just a memset). + * + * NOTE: it is almost inconceivable that this API is used because it bypasses + * the user-memory mechanism and the user error handling/warning mechanisms in + * those cases where it does anything other than a memset. */ - -void PNGAPI -png_info_init_3(png_infopp ptr_ptr, png_size_t png_info_struct_size) +PNG_FUNCTION(void,PNGAPI +png_info_init_3,(png_infopp ptr_ptr, png_size_t png_info_struct_size), + PNG_DEPRECATED) { png_infop info_ptr = *ptr_ptr; @@ -297,13 +414,16 @@ png_info_init_3(png_infopp ptr_ptr, png_size_t png_info_struct_size) if (png_sizeof(png_info) > png_info_struct_size) { - png_destroy_struct(info_ptr); - info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO); + *ptr_ptr = NULL; + /* The following line is why this API should not be used: */ + free(info_ptr); + info_ptr = png_voidcast(png_infop, png_malloc_base(NULL, + sizeof *info_ptr)); *ptr_ptr = info_ptr; } /* Set everything to 0 */ - png_memset(info_ptr, 0, png_sizeof(png_info)); + png_memset(info_ptr, 0, sizeof *info_ptr); } void PNGAPI @@ -548,7 +668,7 @@ png_info_destroy(png_structp png_ptr, png_infop info_ptr) } #endif - png_info_init_3(&info_ptr, png_sizeof(png_info)); + png_memset(info_ptr, 0, sizeof *info_ptr); } #endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ diff --git a/png.h b/png.h index 253b205ef..922a82e5a 100644 --- a/png.h +++ b/png.h @@ -1065,8 +1065,8 @@ PNG_EXPORT(17, void, png_write_chunk_end, (png_structp png_ptr)); PNG_EXPORTA(18, png_infop, png_create_info_struct, (png_structp png_ptr), PNG_ALLOCATED); -PNG_EXPORT(19, void, png_info_init_3, (png_infopp info_ptr, - png_size_t png_info_struct_size)); +PNG_EXPORTA(19, void, png_info_init_3, (png_infopp info_ptr, + png_size_t png_info_struct_size), PNG_DEPRECATED); /* Writes all the PNG information before the image. */ PNG_EXPORT(20, void, png_write_info_before_PLTE, @@ -1894,8 +1894,9 @@ PNG_EXPORT(99, void, png_data_freer, #ifdef PNG_USER_MEM_SUPPORTED PNG_EXPORTA(100, png_voidp, png_malloc_default, (png_structp png_ptr, - png_alloc_size_t size), PNG_ALLOCATED); -PNG_EXPORT(101, void, png_free_default, (png_structp png_ptr, png_voidp ptr)); + png_alloc_size_t size), PNG_ALLOCATED PNG_DEPRECATED); +PNG_EXPORTA(101, void, png_free_default, (png_structp png_ptr, png_voidp ptr), + PNG_DEPRECATED); #endif #ifdef PNG_ERROR_TEXT_SUPPORTED diff --git a/pngerror.c b/pngerror.c index 319a038f3..0b0c518b4 100644 --- a/pngerror.c +++ b/pngerror.c @@ -492,11 +492,108 @@ jmp_buf* PNGAPI png_set_longjmp_fn(png_structp png_ptr, png_longjmp_ptr longjmp_fn, size_t jmp_buf_size) { - if (png_ptr == NULL || jmp_buf_size != png_sizeof(jmp_buf)) + /* From libpng 1.6.0 the app gets one chance to set a 'jmpbuf_size' value + * and it must not change after that. Libpng doesn't care how big the + * buffer is, just that it doesn't change. + * + * If the buffer size is no *larger* than the size of jmp_buf when libpng is + * compiled a built in jmp_buf is returned; this preserves the pre-1.6.0 + * semantics that this call will not fail. If the size is larger, however, + * the buffer is allocated and this may fail, causing the function to return + * NULL. + */ + if (png_ptr == NULL) return NULL; + if (png_ptr->jmp_buf_ptr == NULL) + { + png_ptr->jmp_buf_size = 0; /* not allocated */ + + if (jmp_buf_size <= sizeof png_ptr->jmp_buf_local) + png_ptr->jmp_buf_ptr = &png_ptr->jmp_buf_local; + + else + { + png_ptr->jmp_buf_ptr = png_voidcast(jmp_buf *, + png_malloc_warn(png_ptr, jmp_buf_size)); + + if (png_ptr->jmp_buf_ptr == NULL) + return NULL; /* new NULL return on OOM */ + + png_ptr->jmp_buf_size = jmp_buf_size; + } + } + + else /* Already allocated: check the size */ + { + size_t size = png_ptr->jmp_buf_size; + + if (size == 0) + { + size = sizeof png_ptr->jmp_buf_local; + if (png_ptr->jmp_buf_ptr != &png_ptr->jmp_buf_local) + { + /* This is an internal error in libpng: somehow we have been left + * with a stack allocated jmp_buf when the application regained + * control. It's always possible to fix this up, but for the moment + * this is a png_error because that makes it easy to detect. + */ + png_error(png_ptr, "Libpng jmp_buf still allocated"); + /* png_ptr->jmp_buf_ptr = &png_ptr->jmp_buf_local; */ + } + } + + if (size != jmp_buf_size) + { + png_warning(png_ptr, "Application jmp_buf size changed"); + return NULL; /* caller will probably crash: no choice here */ + } + } + + /* Finally fill in the function, now we have a satisfactory buffer. It is + * valid to change the function on every call. + */ png_ptr->longjmp_fn = longjmp_fn; - return &png_ptr->longjmp_buffer; + return png_ptr->jmp_buf_ptr; +} + +void /* PRIVATE */ +png_free_jmpbuf(png_structp png_ptr) +{ + if (png_ptr != NULL) + { + jmp_buf *jb = png_ptr->jmp_buf_ptr; + + /* A size of 0 is used to indicate a local, stack, allocation of the + * pointer; used here and in png.c + */ + if (jb != NULL && png_ptr->jmp_buf_size > 0) + { + + /* This stuff is so that a failure to free the error control structure + * does not leave libpng in a state with no valid error handling: the + * free always succeeds, if there is an error it gets ignored. + */ + if (jb != &png_ptr->jmp_buf_local) + { + /* Make an internal, libpng, jmp_buf to return here */ + jmp_buf free_jmp_buf; + + if (!setjmp(free_jmp_buf)) + { + png_ptr->jmp_buf_ptr = &free_jmp_buf; /* come back here */ + png_ptr->jmp_buf_size = 0; /* stack allocation */ + png_ptr->longjmp_fn = longjmp; + png_free(png_ptr, jb); /* Return to setjmp on error */ + } + } + } + + /* *Always* cancel everything out: */ + png_ptr->jmp_buf_size = 0; + png_ptr->jmp_buf_ptr = NULL; + png_ptr->longjmp_fn = 0; + } } #endif @@ -556,8 +653,8 @@ PNG_FUNCTION(void,PNGAPI png_longjmp,(png_structp png_ptr, int val),PNG_NORETURN) { #ifdef PNG_SETJMP_SUPPORTED - if (png_ptr && png_ptr->longjmp_fn) - png_ptr->longjmp_fn(png_ptr->longjmp_buffer, val); + if (png_ptr && png_ptr->longjmp_fn && png_ptr->jmp_buf_ptr) + png_ptr->longjmp_fn(*png_ptr->jmp_buf_ptr, val); #endif /* Here if not setjmp support or if png_ptr is null. */ @@ -618,7 +715,7 @@ png_default_warning(png_structp png_ptr, png_const_charp warning_message) /* This function is called when the application wants to use another method * of handling errors and warnings. Note that the error function MUST NOT * return to the calling routine or serious problems will occur. The return - * method used in the default routine calls longjmp(png_ptr->longjmp_buffer, 1) + * method used in the default routine calls longjmp(png_ptr->jmp_buf_ptr, 1) */ void PNGAPI png_set_error_fn(png_structp png_ptr, png_voidp error_ptr, diff --git a/pngmem.c b/pngmem.c index 4c147643e..daed74588 100644 --- a/pngmem.c +++ b/pngmem.c @@ -20,106 +20,23 @@ #include "pngpriv.h" #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) -/* Allocate memory for a png_struct or a png_info. The malloc and - memset can be replaced by a single call to calloc() if this is thought - to improve performance noticably. */ -PNG_FUNCTION(png_voidp /* PRIVATE */, -png_create_struct,(int type),PNG_ALLOCATED) -{ -# ifdef PNG_USER_MEM_SUPPORTED - return (png_create_struct_2(type, NULL, NULL)); -} - -/* Allocate memory for a png_struct or a png_info. The malloc and - memset can be replaced by a single call to calloc() if this is thought - to improve performance noticably. */ -PNG_FUNCTION(png_voidp /* PRIVATE */, -png_create_struct_2,(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr), - PNG_ALLOCATED) -{ -# endif /* PNG_USER_MEM_SUPPORTED */ - png_size_t size; - png_voidp struct_ptr; - - if (type == PNG_STRUCT_INFO) - size = png_sizeof(png_info); - - else if (type == PNG_STRUCT_PNG) - size = png_sizeof(png_struct); - - else - return (NULL); - -# ifdef PNG_USER_MEM_SUPPORTED - if (malloc_fn != NULL) - { - png_struct dummy_struct; - png_structp png_ptr = &dummy_struct; - png_ptr->mem_ptr=mem_ptr; - struct_ptr = (*(malloc_fn))(png_ptr, size); - - if (struct_ptr != NULL) - png_memset(struct_ptr, 0, size); - - return (struct_ptr); - } -# endif /* PNG_USER_MEM_SUPPORTED */ - -# if defined(__TURBOC__) && !defined(__FLAT__) - struct_ptr = (png_voidp)farmalloc(size); -# else -# if defined(_MSC_VER) && defined(MAXSEG_64K) - struct_ptr = (png_voidp)halloc(size, 1); -# else - struct_ptr = (png_voidp)malloc(size); -# endif -# endif - - if (struct_ptr != NULL) - png_memset(struct_ptr, 0, size); - - return (struct_ptr); -} - - -/* Free memory allocated by a png_create_struct() call */ +/* Free a png_struct */ void /* PRIVATE */ -png_destroy_struct(png_voidp struct_ptr) +png_destroy_png_struct(png_structp png_ptr) { -# ifdef PNG_USER_MEM_SUPPORTED - png_destroy_struct_2(struct_ptr, NULL, NULL); -} - -/* Free memory allocated by a png_create_struct() call */ -void /* PRIVATE */ -png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn, - png_voidp mem_ptr) -{ -# endif /* PNG_USER_MEM_SUPPORTED */ - if (struct_ptr != NULL) + if (png_ptr != NULL) { -# ifdef PNG_USER_MEM_SUPPORTED - if (free_fn != NULL) - { - png_struct dummy_struct; - png_structp png_ptr = &dummy_struct; - png_ptr->mem_ptr=mem_ptr; - (*(free_fn))(png_ptr, struct_ptr); - return; - } -# endif /* PNG_USER_MEM_SUPPORTED */ -# if defined(__TURBOC__) && !defined(__FLAT__) - farfree(struct_ptr); + /* png_free might call png_error and may certainly call + * png_get_mem_ptr, so fake a temporary png_struct to support this. + */ + png_struct dummy_struct = *png_ptr; + memset(png_ptr, 0, sizeof *png_ptr); + png_free(&dummy_struct, png_ptr); -# else -# if defined(_MSC_VER) && defined(MAXSEG_64K) - hfree(struct_ptr); - -# else - free(struct_ptr); - -# endif -# endif +# ifdef PNG_SETJMP_SUPPORTED + /* We may have a jmp_buf left to deallocate. */ + png_free_jmpbuf(&dummy_struct); +# endif } } @@ -129,95 +46,107 @@ png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn, * need to allocate exactly 64K, so whatever you call here must * have the ability to do that. */ - PNG_FUNCTION(png_voidp,PNGAPI png_calloc,(png_structp png_ptr, png_alloc_size_t size),PNG_ALLOCATED) { png_voidp ret; - ret = (png_malloc(png_ptr, size)); + ret = png_malloc(png_ptr, size); if (ret != NULL) - png_memset(ret,0,(png_size_t)size); + png_memset(ret, 0, size); - return (ret); + return ret; } +/* png_malloc_base, an internal function added at libpng 1.6.0, does the work of + * allocating memory, taking into account limits and PNG_USER_MEM_SUPPORTED. + * Checking and error handling must happen outside this routine; it returns NULL + * if the allocation cannot be done (for any reason.) + */ +PNG_FUNCTION(png_voidp /* PRIVATE */, +png_malloc_base,(png_structp png_ptr, png_alloc_size_t size),PNG_ALLOCATED) +{ + /* Moved to png_malloc_base from png_malloc_default in 1.6.0; the DOS + * allocators have also been removed in 1.6.0, so any 16-bit system now has + * to implement a user memory handler. This checks to be sure it isn't + * called with big numbers. + */ + if (size > 0 && size <= ~(size_t)0 +# ifdef PNG_MAX_MALLOC_64K + && size <= 65536U +# endif + ) + { + if (png_ptr != NULL && png_ptr->malloc_fn != NULL) + return png_ptr->malloc_fn(png_ptr, size); + + else + return malloc((size_t)size); /* checked for truncation above */ + } + + else + return NULL; +} + +/* Various functions that have different error handling are derived from this. + * png_malloc always exists, but if PNG_USER_MEM_SUPPORTED is defined a separate + * function png_malloc_default is also provided. + */ PNG_FUNCTION(png_voidp,PNGAPI png_malloc,(png_structp png_ptr, png_alloc_size_t size),PNG_ALLOCATED) { png_voidp ret; -# ifdef PNG_USER_MEM_SUPPORTED - if (png_ptr == NULL || size == 0) - return (NULL); + if (png_ptr == NULL) + return NULL; - if (png_ptr->malloc_fn != NULL) - ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size)); + ret = png_malloc_base(png_ptr, size); - else - ret = (png_malloc_default(png_ptr, size)); + if (ret == NULL) + png_error(png_ptr, "Out of memory"); /* 'm' means png_malloc */ - if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) - png_error(png_ptr, "Out of Memory"); - - return (ret); + return ret; } +#ifdef PNG_USER_MEM_SUPPORTED PNG_FUNCTION(png_voidp,PNGAPI -png_malloc_default,(png_structp png_ptr, png_alloc_size_t size),PNG_ALLOCATED) +png_malloc_default,(png_structp png_ptr, png_alloc_size_t size), + PNG_ALLOCATED PNG_DEPRECATED) { png_voidp ret; -# endif /* PNG_USER_MEM_SUPPORTED */ - if (png_ptr == NULL || size == 0) - return (NULL); + if (png_ptr == NULL) + return NULL; -# ifdef PNG_MAX_MALLOC_64K - if (size > (png_uint_32)65536L) + /* Passing 'NULL' here bypasses the application provided memory handler. */ + ret = png_malloc_base(NULL/*use malloc*/, size); + + if (ret == NULL) + png_error(png_ptr, "Out of Memory"); /* 'M' means png_malloc_default */ + + return ret; +} +#endif /* PNG_USER_MEM_SUPPORTED */ + +/* This function was added at libpng version 1.2.3. The png_malloc_warn() + * function will issue a png_warning and return NULL instead of issuing a + * png_error, if it fails to allocate the requested memory. + */ +PNG_FUNCTION(png_voidp,PNGAPI +png_malloc_warn,(png_structp png_ptr, png_alloc_size_t size),PNG_ALLOCATED) +{ + if (png_ptr != NULL) { -# ifndef PNG_USER_MEM_SUPPORTED - if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) - png_error(png_ptr, "Cannot Allocate > 64K"); + png_voidp ret = png_malloc_base(png_ptr, size); - else -# endif - return NULL; + if (ret != NULL) + return ret; + + png_warning(png_ptr, "Out of memory"); } -# endif - /* Check for overflow */ -# if defined(__TURBOC__) && !defined(__FLAT__) - - if (size != (unsigned long)size) - ret = NULL; - - else - ret = farmalloc(size); - -# else -# if defined(_MSC_VER) && defined(MAXSEG_64K) - if (size != (unsigned long)size) - ret = NULL; - - else - ret = halloc(size, 1); - -# else - if (size != (size_t)size) - ret = NULL; - - else - ret = malloc((size_t)size); -# endif -# endif - -# ifndef PNG_USER_MEM_SUPPORTED - if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) - png_error(png_ptr, "Out of Memory"); -# endif - - return (ret); + return NULL; } /* Free a pointer allocated by png_malloc(). If ptr is NULL, return @@ -229,60 +158,24 @@ png_free(png_structp png_ptr, png_voidp ptr) if (png_ptr == NULL || ptr == NULL) return; -# ifdef PNG_USER_MEM_SUPPORTED +#ifdef PNG_USER_MEM_SUPPORTED if (png_ptr->free_fn != NULL) - { - (*(png_ptr->free_fn))(png_ptr, ptr); - return; - } + png_ptr->free_fn(png_ptr, ptr); else png_free_default(png_ptr, ptr); } -void PNGAPI -png_free_default(png_structp png_ptr, png_voidp ptr) +PNG_FUNCTION(void,PNGAPI +png_free_default,(png_structp png_ptr, png_voidp ptr),PNG_DEPRECATED) { if (png_ptr == NULL || ptr == NULL) return; +#endif /* PNG_USER_MEM_SUPPORTED */ -# endif /* PNG_USER_MEM_SUPPORTED */ - -# if defined(__TURBOC__) && !defined(__FLAT__) - farfree(ptr); - -# else -# if defined(_MSC_VER) && defined(MAXSEG_64K) - hfree(ptr); - -# else free(ptr); - -# endif -# endif } -/* This function was added at libpng version 1.2.3. The png_malloc_warn() - * function will set up png_malloc() to issue a png_warning and return NULL - * instead of issuing a png_error, if it fails to allocate the requested - * memory. - */ -PNG_FUNCTION(png_voidp,PNGAPI -png_malloc_warn,(png_structp png_ptr, png_alloc_size_t size),PNG_ALLOCATED) -{ - png_voidp ptr; - png_uint_32 save_flags; - if (png_ptr == NULL) - return (NULL); - - save_flags = png_ptr->flags; - png_ptr->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK; - ptr = (png_voidp)png_malloc((png_structp)png_ptr, size); - png_ptr->flags=save_flags; - return(ptr); -} - - #ifdef PNG_USER_MEM_SUPPORTED /* This function is called when the application wants to use another method * of allocating and freeing memory. @@ -307,9 +200,9 @@ png_voidp PNGAPI png_get_mem_ptr(png_const_structp png_ptr) { if (png_ptr == NULL) - return (NULL); + return NULL; - return ((png_voidp)png_ptr->mem_ptr); + return png_ptr->mem_ptr; } #endif /* PNG_USER_MEM_SUPPORTED */ #endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/pngpriv.h b/pngpriv.h index 83da3c42f..3e8c5e1c4 100644 --- a/pngpriv.h +++ b/pngpriv.h @@ -489,7 +489,7 @@ typedef const png_uint_16p * png_const_uint_16pp; #define PNG_FLAG_LIBRARY_MISMATCH 0x20000 #define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000 #define PNG_FLAG_STRIP_ERROR_TEXT 0x80000 -#define PNG_FLAG_MALLOC_NULL_MEM_OK 0x100000 + /* 0x100000 unused */ /* 0x200000 unused */ /* 0x400000 unused */ #define PNG_FLAG_BENIGN_ERRORS_WARN 0x800000 /* Added to libpng-1.4.0 */ @@ -677,35 +677,48 @@ PNG_EXTERN png_fixed_point png_fixed PNGARG((png_structp png_ptr, double fp, extern "C" { #endif /* __cplusplus */ -/* These functions are used internally in the code. They generally - * shouldn't be used unless you are writing code to add or replace some - * functionality in libpng. More information about most functions can - * be found in the files where the functions are located. +/* Internal functions; these are not exported from a DLL however because they + * are used within several of the C source files they have to be C extern. */ /* Check the user version string for compatibility, returns false if the version * numbers aren't compatible. */ -PNG_EXTERN int png_user_version_check(png_structp png_ptr, - png_const_charp user_png_ver); +PNG_EXTERN int png_user_version_check PNGARG((png_structp png_ptr, + png_const_charp user_png_ver)); -/* Allocate memory for an internal libpng struct */ -PNG_EXTERN PNG_FUNCTION(png_voidp,png_create_struct,PNGARG((int type)), - PNG_ALLOCATED); +/* Internal base allocator - no messages, NULL on failure to allocate. This + * does, however, call the application provided allocator and that could call + * png_error (although that would be a bug in the application implementation.) + */ +PNG_EXTERN PNG_FUNCTION(png_voidp,png_malloc_base,PNGARG((png_structp png_ptr, + png_alloc_size_t size)),PNG_ALLOCATED); + +/* Magic to create a struct when there is no struct to call the user supplied + * memory allocators. Because error handling has not been set up the memory + * handlers can't safely call png_error, but this is an obscure and undocumented + * restriction so libpng has to assume that the 'free' handler, at least, might + * call png_error. + */ +PNG_EXTERN PNG_FUNCTION(png_structp,png_create_png_struct, + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)),PNG_ALLOCATED); /* Free memory from internal libpng struct */ -PNG_EXTERN void png_destroy_struct PNGARG((png_voidp struct_ptr)); - -PNG_EXTERN PNG_FUNCTION(png_voidp,png_create_struct_2, - PNGARG((int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr)), - PNG_ALLOCATED); -PNG_EXTERN void png_destroy_struct_2 PNGARG((png_voidp struct_ptr, - png_free_ptr free_fn, png_voidp mem_ptr)); +#if 0 /* no longer used */ +PNG_EXTERN void png_destroy_struct_2 PNGARG((png_structp png_ptr, + png_voidp struct_ptr)); +#endif +PNG_EXTERN void png_destroy_png_struct PNGARG((png_structp png_ptr)); /* Free any memory that info_ptr points to and reset struct. */ PNG_EXTERN void png_info_destroy PNGARG((png_structp png_ptr, png_infop info_ptr)); +/* Free an allocated jmp_buf (always succeeds) */ +PNG_EXTERN void png_free_jmpbuf PNGARG((png_structp png_ptr)); + /* Function to allocate memory for zlib. PNGAPI is disallowed. */ PNG_EXTERN PNG_FUNCTION(voidpf,png_zalloc,PNGARG((voidpf png_ptr, uInt items, uInt size)),PNG_ALLOCATED); @@ -1375,13 +1388,6 @@ PNG_EXTERN void png_check_IHDR PNGARG((png_structp png_ptr, int color_type, int interlace_type, int compression_type, int filter_type)); -/* Free all memory used by the read (old method - NOT DLL EXPORTED) */ -PNG_EXTERN void png_read_destroy PNGARG((png_structp png_ptr, - png_infop info_ptr, png_infop end_info_ptr)); - -/* Free any memory used in png_ptr struct (old method - NOT DLL EXPORTED) */ -PNG_EXTERN void png_write_destroy PNGARG((png_structp png_ptr)); - #if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED) PNG_EXTERN PNG_FUNCTION(void, png_fixed_error, (png_structp png_ptr, png_const_charp name),PNG_NORETURN); diff --git a/pngstruct.h b/pngstruct.h index 07f3a0425..da7ad0c6e 100644 --- a/pngstruct.h +++ b/pngstruct.h @@ -29,8 +29,10 @@ struct png_struct_def { #ifdef PNG_SETJMP_SUPPORTED - jmp_buf longjmp_buffer; /* used in png_error */ png_longjmp_ptr longjmp_fn;/* setjmp non-local goto function. */ + jmp_buf *jmp_buf_ptr; /* passed to longjmp_fn */ + size_t jmp_buf_size; /* size of the above, if allocated */ + jmp_buf jmp_buf_local; /* New name in 1.6.0 for jmp_buf in png_struct */ #endif png_error_ptr error_fn; /* function for printing errors and aborting */ #ifdef PNG_WARNINGS_SUPPORTED diff --git a/pngwrite.c b/pngwrite.c index bc68d5661..d13cd39f7 100644 --- a/pngwrite.c +++ b/pngwrite.c @@ -449,92 +449,34 @@ PNG_FUNCTION(png_structp,PNGAPI png_create_write_struct,(png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn),PNG_ALLOCATED) { -#ifdef PNG_USER_MEM_SUPPORTED - return (png_create_write_struct_2(user_png_ver, error_ptr, error_fn, - warn_fn, NULL, NULL, NULL)); +#ifndef PNG_USER_MEM_SUPPORTED + png_structp png_ptr = png_create_png_struct(user_png_ver, error_ptr, + error_fn, warn_fn, NULL, NULL, NULL); +#else + return png_create_write_struct_2(user_png_ver, error_ptr, error_fn, + warn_fn, NULL, NULL, NULL); } /* Alternate initialize png_ptr structure, and allocate any memory needed */ -static void png_reset_filter_heuristics(png_structp png_ptr); /* forward decl */ - PNG_FUNCTION(png_structp,PNGAPI png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED) { + png_structp png_ptr = png_create_png_struct(user_png_ver, error_ptr, + error_fn, warn_fn, mem_ptr, malloc_fn, free_fn); #endif /* PNG_USER_MEM_SUPPORTED */ - volatile int png_cleanup_needed = 0; -#ifdef PNG_SETJMP_SUPPORTED - volatile -#endif - png_structp png_ptr; - png_debug(1, "in png_create_write_struct"); - -#ifdef PNG_USER_MEM_SUPPORTED - png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG, - (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr); -#else - png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); -#endif /* PNG_USER_MEM_SUPPORTED */ - if (png_ptr == NULL) - return (NULL); - - /* Added at libpng-1.2.6 */ -#ifdef PNG_SET_USER_LIMITS_SUPPORTED - png_ptr->user_width_max = PNG_USER_WIDTH_MAX; - png_ptr->user_height_max = PNG_USER_HEIGHT_MAX; -#endif - -#ifdef PNG_SETJMP_SUPPORTED -/* Applications that neglect to set up their own setjmp() and then - * encounter a png_error() will longjmp here. Since the jmpbuf is - * then meaningless we abort instead of returning. - */ - if (setjmp(png_jmpbuf(png_ptr))) /* sets longjmp to match setjmp */ - PNG_ABORT(); -#endif - -#ifdef PNG_USER_MEM_SUPPORTED - png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn); -#endif /* PNG_USER_MEM_SUPPORTED */ - png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn); - - if (!png_user_version_check(png_ptr, user_png_ver)) - png_cleanup_needed = 1; - - /* Initialize zbuf - compression buffer */ - png_ptr->zbuf_size = PNG_ZBUF_SIZE; - - if (!png_cleanup_needed) + if (png_ptr != NULL) { - png_ptr->zbuf = (png_bytep)png_malloc_warn(png_ptr, - png_ptr->zbuf_size); - if (png_ptr->zbuf == NULL) - png_cleanup_needed = 1; + /* TODO: delay this, it can be done in png_init_io() (if the app doesn't + * do it itself) avoiding setting the default function if it is not + * required. + */ + png_set_write_fn(png_ptr, NULL, NULL, NULL); } - if (png_cleanup_needed) - { - /* Clean up PNG structure and deallocate any memory. */ - png_free(png_ptr, png_ptr->zbuf); - png_ptr->zbuf = NULL; -#ifdef PNG_USER_MEM_SUPPORTED - png_destroy_struct_2((png_voidp)png_ptr, - (png_free_ptr)free_fn, (png_voidp)mem_ptr); -#else - png_destroy_struct((png_voidp)png_ptr); -#endif - return (NULL); - } - - png_set_write_fn(png_ptr, NULL, NULL, NULL); - -#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED - png_reset_filter_heuristics(png_ptr); -#endif - - return (png_ptr); + return png_ptr; } @@ -862,87 +804,14 @@ png_write_flush(png_structp png_ptr) } #endif /* PNG_WRITE_FLUSH_SUPPORTED */ -/* Free all memory used by the write */ -void PNGAPI -png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr) -{ - png_structp png_ptr = NULL; - png_infop info_ptr = NULL; -#ifdef PNG_USER_MEM_SUPPORTED - png_free_ptr free_fn = NULL; - png_voidp mem_ptr = NULL; +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +static void png_reset_filter_heuristics(png_structp png_ptr); /* forward decl */ #endif - png_debug(1, "in png_destroy_write_struct"); - - if (png_ptr_ptr != NULL) - png_ptr = *png_ptr_ptr; - -#ifdef PNG_USER_MEM_SUPPORTED - if (png_ptr != NULL) - { - free_fn = png_ptr->free_fn; - mem_ptr = png_ptr->mem_ptr; - } -#endif - - if (info_ptr_ptr != NULL) - info_ptr = *info_ptr_ptr; - - if (info_ptr != NULL) - { - if (png_ptr != NULL) - { - png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); - -#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED - if (png_ptr->num_chunk_list) - { - png_free(png_ptr, png_ptr->chunk_list); - png_ptr->num_chunk_list = 0; - } -#endif - } - -#ifdef PNG_USER_MEM_SUPPORTED - png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn, - (png_voidp)mem_ptr); -#else - png_destroy_struct((png_voidp)info_ptr); -#endif - *info_ptr_ptr = NULL; - } - - if (png_ptr != NULL) - { - png_write_destroy(png_ptr); -#ifdef PNG_USER_MEM_SUPPORTED - png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn, - (png_voidp)mem_ptr); -#else - png_destroy_struct((png_voidp)png_ptr); -#endif - *png_ptr_ptr = NULL; - } -} - - -/* Free any memory used in png_ptr struct (old method) */ -void /* PRIVATE */ +/* Free any memory used in png_ptr struct without freeing the struct itself. */ +static void png_write_destroy(png_structp png_ptr) { -#ifdef PNG_SETJMP_SUPPORTED - jmp_buf tmp_jmp; /* Save jump buffer */ -#endif - png_error_ptr error_fn; -#ifdef PNG_WARNINGS_SUPPORTED - png_error_ptr warning_fn; -#endif - png_voidp error_ptr; -#ifdef PNG_USER_MEM_SUPPORTED - png_free_ptr free_fn; -#endif - png_debug(1, "in png_write_destroy"); /* Free any memory zlib uses */ @@ -967,34 +836,41 @@ png_write_destroy(png_structp png_ptr) png_free(png_ptr, png_ptr->inv_filter_costs); #endif -#ifdef PNG_SETJMP_SUPPORTED - /* Reset structure */ - png_memcpy(tmp_jmp, png_ptr->longjmp_buffer, png_sizeof(jmp_buf)); +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + png_free(png_ptr, png_ptr->chunk_list); #endif - error_fn = png_ptr->error_fn; -#ifdef PNG_WARNINGS_SUPPORTED - warning_fn = png_ptr->warning_fn; -#endif - error_ptr = png_ptr->error_ptr; -#ifdef PNG_USER_MEM_SUPPORTED - free_fn = png_ptr->free_fn; -#endif + /* The error handling and memory handling information is left intact at this + * point: the jmp_buf may still have to be freed. See png_destroy_png_struct + * for how this happens. + */ +} - png_memset(png_ptr, 0, png_sizeof(png_struct)); +/* Free all memory used by the write. + * In libpng 1.6.0 this API changed quietly to no longer accept a NULL value for + * *png_ptr_ptr. Prior to 1.6.0 it would accept such a value and it would free + * the passed in info_structs but it would quietly fail to free any of the data + * inside them. In 1.6.0 it quietly does nothing (it has to be quiet because it + * has no png_ptr.) + */ +void PNGAPI +png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr) +{ + png_debug(1, "in png_destroy_write_struct"); - png_ptr->error_fn = error_fn; -#ifdef PNG_WARNINGS_SUPPORTED - png_ptr->warning_fn = warning_fn; -#endif - png_ptr->error_ptr = error_ptr; -#ifdef PNG_USER_MEM_SUPPORTED - png_ptr->free_fn = free_fn; -#endif + if (png_ptr_ptr != NULL) + { + png_structp png_ptr = *png_ptr_ptr; -#ifdef PNG_SETJMP_SUPPORTED - png_memcpy(png_ptr->longjmp_buffer, tmp_jmp, png_sizeof(jmp_buf)); -#endif + if (png_ptr != NULL) /* added in libpng 1.6.0 */ + { + png_destroy_info_struct(png_ptr, info_ptr_ptr); + + *png_ptr_ptr = NULL; + png_write_destroy(png_ptr); + png_destroy_png_struct(png_ptr); + } + } } /* Allow the application to select one or more row filters to use. */