diff options
-rw-r--r-- | release_docs/RELEASE.txt | 27 | ||||
-rw-r--r-- | src/H5.c | 89 | ||||
-rw-r--r-- | src/H5MM.c | 121 | ||||
-rw-r--r-- | src/H5MMprivate.h | 6 | ||||
-rw-r--r-- | src/H5public.h | 2 | ||||
-rw-r--r-- | test/testhdf5.h | 12 | ||||
-rw-r--r-- | test/tmisc.c | 68 |
7 files changed, 263 insertions, 62 deletions
diff --git a/release_docs/RELEASE.txt b/release_docs/RELEASE.txt index 7b866ce..52c4ae5 100644 --- a/release_docs/RELEASE.txt +++ b/release_docs/RELEASE.txt @@ -185,7 +185,32 @@ New Features Library ------- - - None + - Added memory allocation functions that use the library's allocator. + + HDF5 filters may need to allocate or resize the buffer that is passed + to them from the library. If the filter has been compiled separately + from the library, it and the library may use different memory + allocation libraries for the (re)allocation and free calls. This can + cause heap corruption and crashes. This is particularly a problem on + Windows since each C run-time library is implemented as a separate + shared library, but can also show up on POSIX systems when debug or + high-performance allocation libraries are in use. + + Two new functions (H5allocate_memory() and H5resize_memory()) were + added to the HDF5 C library. These functions have the same semantics as + malloc/calloc and realloc, respectively. Their primary purpose is to + allow filter authors to allocate or resize memory using the same + memory allocation library as the HDF5 library. Filter authors are + highly encouraged to use these new functions in place of malloc, + calloc, and realloc. They should also use the H5free_memory() call when + freeing memory. + + Note that the filters provided with the library (zlib, szip, etc.) do + not experience the problems that these new functions are intended to + fix. This work only applies to third-party filters that are compiled + separately from the library. + + (DER - 2015-04-01, HDFFV-9100) Parallel Library ---------------- @@ -858,27 +858,109 @@ H5close(void) /*------------------------------------------------------------------------- + * Function: H5allocate_memory + * + * Purpose: Allocate a memory buffer with the semantics of malloc(). + * + * NOTE: This function is intended for use with filter + * plugins so that all allocation and free operations + * use the same memory allocator. It is not intended for + * use as a general memory allocator in applications. + * + * Parameters: + * + * size: The size of the buffer. + * + * clear: Whether or not to memset the buffer to 0. + * + * Return: + * + * Success: A pointer to the allocated buffer. + * + * Failure: NULL + * + *------------------------------------------------------------------------- + */ +void * +H5allocate_memory(size_t size, hbool_t clear) +{ + void *ret_value = NULL; + + FUNC_ENTER_API_NOINIT; + H5TRACE2("*x", "zb", size, clear); + + if(clear) + ret_value = H5MM_calloc(size); + else + ret_value = H5MM_malloc(size); + + FUNC_LEAVE_API(ret_value) + +} /* end H5allocate_memory() */ + + +/*------------------------------------------------------------------------- + * Function: H5resize_memory + * + * Purpose: Resize a memory buffer with the semantics of realloc(). + * + * NOTE: This function is intended for use with filter + * plugins so that all allocation and free operations + * use the same memory allocator. It is not intended for + * use as a general memory allocator in applications. + * + * Parameters: + * + * mem: The buffer to be resized. + * + * size: The size of the buffer. + * + * Return: + * + * Success: A pointer to the resized buffer. + * + * Failure: NULL (the input buffer will be unchanged) + * + *------------------------------------------------------------------------- + */ +void * +H5resize_memory(void *mem, size_t size) +{ + void *ret_value = NULL; + + FUNC_ENTER_API_NOINIT; + H5TRACE2("*x", "*xz", mem, size); + + ret_value = H5MM_realloc(mem, size); + + FUNC_LEAVE_API(ret_value) + +} /* end H5resize_memory() */ + + +/*------------------------------------------------------------------------- * Function: H5free_memory * - * Purpose: Frees memory allocated by the library that it is the user's + * Purpose: Frees memory allocated by the library that it is the user's * responsibility to free. Ensures that the same library * that was used to allocate the memory frees it. Passing * NULL pointers is allowed. * - * Return: SUCCEED/FAIL + * Return: SUCCEED/FAIL * *------------------------------------------------------------------------- */ herr_t H5free_memory(void *mem) { - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_NOINIT; H5TRACE1("e", "*x", mem); /* At this time, it is impossible for this to fail. */ HDfree(mem); FUNC_LEAVE_API(SUCCEED) + } /* end H5free_memory() */ @@ -939,3 +1021,4 @@ DllMain(_In_ HINSTANCE hinstDLL, _In_ DWORD fdwReason, _In_ LPVOID lpvReserved) return fOkay; } #endif /* H5_HAVE_WIN32_API && H5_BUILT_AS_DYNAMIC_LIB && H5_HAVE_WIN_THREADS && H5_HAVE_THREADSAFE*/ + @@ -31,93 +31,107 @@ #include "H5Eprivate.h" #include "H5MMprivate.h" -#ifndef NDEBUG /*------------------------------------------------------------------------- - * Function: H5MM_malloc + * Function: H5MM_malloc * - * Purpose: Just like the POSIX version of malloc(3). This routine - * specifically checks for allocations of 0 bytes and fails - * in that case. This routine is not called when NDEBUG is - * defined. + * Purpose: Similar to the C89 version of malloc(). * - * Return: Success: Ptr to new memory + * On size of 0, we return a NULL pointer instead of the + * standard-allowed 'special' pointer since that's more + * difficult to check as a return value. This is still + * considered an error condition since allocations of zero + * bytes usually indicate problems. + * + * Return: Success: Pointer new memory * - * Failure: NULL + * Failure: NULL * - * Programmer: Quincey Koziol - * koziol@ncsa.uiuc.edu - * Nov 8 2003 - * - * Modifications: + * Programmer: Quincey Koziol + * Nov 8 2003 * *------------------------------------------------------------------------- */ void * H5MM_malloc(size_t size) { + void *ret_value; + + HDassert(size); + /* Use FUNC_ENTER_NOAPI_NOINIT_NOERR here to avoid performance issues */ FUNC_ENTER_NOAPI_NOINIT_NOERR - HDassert(size); + if(size) + ret_value = HDmalloc(size); + else + ret_value = NULL; - FUNC_LEAVE_NOAPI(HDmalloc(size)); + FUNC_LEAVE_NOAPI(ret_value); } /* end H5MM_malloc() */ /*------------------------------------------------------------------------- - * Function: H5MM_calloc + * Function: H5MM_calloc * - * Purpose: Similar to the POSIX version of calloc(3), except this routine - * just takes a 'size' parameter. This routine - * specifically checks for allocations of 0 bytes and fails - * in that case. This routine is not called when NDEBUG is - * defined. + * Purpose: Similar to the C89 version of calloc(), except this + * routine just takes a 'size' parameter. * - * Return: Success: Ptr to new memory + * On size of 0, we return a NULL pointer instead of the + * standard-allowed 'special' pointer since that's more + * difficult to check as a return value. This is still + * considered an error condition since allocations of zero + * bytes usually indicate problems. * - * Failure: NULL * - * Programmer: Quincey Koziol - * koziol@ncsa.uiuc.edu - * Nov 8 2003 + * Return: Success: Pointer new memory * - * Modifications: + * Failure: NULL + * + * Programmer: Quincey Koziol + * Nov 8 2003 * *------------------------------------------------------------------------- */ void * H5MM_calloc(size_t size) { + void *ret_value; + + HDassert(size); + /* Use FUNC_ENTER_NOAPI_NOINIT_NOERR here to avoid performance issues */ FUNC_ENTER_NOAPI_NOINIT_NOERR - HDassert(size); + if(size) + ret_value = HDcalloc((size_t)1, size); + else + ret_value = NULL; - FUNC_LEAVE_NOAPI(HDcalloc(1,size)); + FUNC_LEAVE_NOAPI(ret_value); } /* end H5MM_calloc() */ -#endif /* NDEBUG */ /*------------------------------------------------------------------------- - * Function: H5MM_realloc + * Function: H5MM_realloc * - * Purpose: Just like the POSIX version of realloc(3). Specifically, the - * following calls are equivalent + * Purpose: Similar semantics as C89's realloc(). Specifically, the + * following calls are equivalent: * - * H5MM_realloc (NULL, size) <==> H5MM_malloc (size) - * H5MM_realloc (ptr, 0) <==> H5MM_xfree (ptr) - * H5MM_realloc (NULL, 0) <==> NULL + * H5MM_realloc(NULL, size) <==> H5MM_malloc(size) + * H5MM_realloc(ptr, 0) <==> H5MM_xfree(ptr) + * H5MM_realloc(NULL, 0) <==> NULL * - * Return: Success: Ptr to new memory or NULL if the memory - * was freed or HDrealloc couldn't allocate - * memory. + * Note that the (NULL, 0) combination is undefined behavior + * in the C standard. * - * Failure: NULL + * Return: Success: Ptr to new memory if size > 0 + * NULL if size is zero * - * Programmer: Robb Matzke - * matzke@llnl.gov - * Jul 10 1997 + * Failure: NULL (input buffer is unchanged on failure) + * + * Programmer: Robb Matzke + * Jul 10 1997 * *------------------------------------------------------------------------- */ @@ -129,16 +143,19 @@ H5MM_realloc(void *mem, size_t size) /* Use FUNC_ENTER_NOAPI_NOINIT_NOERR here to avoid performance issues */ FUNC_ENTER_NOAPI_NOINIT_NOERR - if(NULL == mem) { - if(0 == size) + HDassert(mem || size); + + if(NULL == mem && 0 == size) { + /* Not defined in the standard, return NULL */ + ret_value = NULL; + } + else { + ret_value = HDrealloc(mem, size); + + /* Some platforms do not return NULL if size is zero. */ + if(0 == size) ret_value = NULL; - else - ret_value = H5MM_malloc(size); - } /* end if */ - else if(0 == size) - ret_value = H5MM_xfree(mem); - else - ret_value = HDrealloc(mem, size); + } FUNC_LEAVE_NOAPI(ret_value) } /* end H5MM_realloc() */ diff --git a/src/H5MMprivate.h b/src/H5MMprivate.h index a3c39f0..0d608b2 100644 --- a/src/H5MMprivate.h +++ b/src/H5MMprivate.h @@ -33,19 +33,13 @@ /* Private headers needed by this file */ #include "H5private.h" -#ifdef NDEBUG -#define H5MM_malloc(Z) HDmalloc(Z) -#define H5MM_calloc(Z) HDcalloc((size_t)1,Z) -#endif /* NDEBUG */ #define H5MM_free(Z) HDfree(Z) /* * Library prototypes... */ -#ifndef NDEBUG H5_DLL void *H5MM_malloc(size_t size); H5_DLL void *H5MM_calloc(size_t size); -#endif /* NDEBUG */ H5_DLL void *H5MM_realloc(void *mem, size_t size); H5_DLL char *H5MM_xstrdup(const char *s); H5_DLL char *H5MM_strdup(const char *s); diff --git a/src/H5public.h b/src/H5public.h index 2987133..fb73ed6 100644 --- a/src/H5public.h +++ b/src/H5public.h @@ -332,6 +332,8 @@ H5_DLL herr_t H5get_libversion(unsigned *majnum, unsigned *minnum, H5_DLL herr_t H5check_version(unsigned majnum, unsigned minnum, unsigned relnum); H5_DLL herr_t H5free_memory(void *mem); +H5_DLL void *H5allocate_memory(size_t size, hbool_t clear); +H5_DLL void *H5resize_memory(void *mem, size_t size); #ifdef __cplusplus } diff --git a/test/testhdf5.h b/test/testhdf5.h index 907fce9..62dadde 100644 --- a/test/testhdf5.h +++ b/test/testhdf5.h @@ -68,6 +68,18 @@ } \ } +#define CHECK_PTR_NULL(ret,where) { \ + if (VERBOSE_HI) { \ + print_func(" Call to routine: %15s at line %4d in %s returned %p\n", \ + (where), (int)__LINE__, __FILE__, (ret)); \ + } \ + if (ret) { \ + TestErrPrintf ("*** UNEXPECTED RETURN from %s is not NULL line %4d in %s\n", \ + (where), (int)__LINE__, __FILE__); \ + H5Eprint2(H5E_DEFAULT, stdout); \ + } \ +} + /* Used to make certain a return value _is_ a value */ #define VERIFY(_x, _val, where) do { \ long __x = (long)_x, __val = (long)_val; \ diff --git a/test/tmisc.c b/test/tmisc.c index 45e85cc..d606438 100644 --- a/test/tmisc.c +++ b/test/tmisc.c @@ -5360,6 +5360,73 @@ test_misc31(void) /**************************************************************** + * + * test_misc32(): Simple test of filter memory allocation + * functions. + * + ***************************************************************/ +static void +test_misc32(void) +{ + void *buffer; + void *resized; + size_t size; + + /* Output message about test being performed */ + MESSAGE(5, ("Edge case test of filter memory allocation functions\n")); + + /* Test that the filter memory allocation functions behave correctly + * at edge cases. + */ + + /* FREE */ + + /* Test freeing a NULL pointer. + * No real confirmation check here, but Valgrind will confirm no + * shenanigans. + */ + buffer = NULL; + H5free_memory(buffer); + + /* ALLOCATE */ + + /* Size zero returns NULL. + * Also checks that a size of zero and setting the buffer clear flag + * to TRUE can be used together. + * + * Note that we have asserts in the code, so only check when NDEBUG + * is defined. + */ +#ifdef NDEBUG + buffer = H5allocate_memory(0, FALSE); + CHECK_PTR_NULL(buffer, "H5allocate_memory"); /*BAD*/ + buffer = H5allocate_memory(0, TRUE); + CHECK_PTR_NULL(buffer, "H5allocate_memory"); /*BAD*/ +#endif /* NDEBUG */ + + /* RESIZE */ + + /* Size zero returns NULL. Valgrind will confirm buffer is freed. */ + size = 1024; + buffer = H5allocate_memory(size, TRUE); + resized = H5resize_memory(buffer, 0); + CHECK_PTR_NULL(resized, "H5resize_memory"); + + /* NULL input pointer returns new buffer */ + resized = H5resize_memory(NULL, 1024); + CHECK_PTR(resized, "H5resize_memory"); + H5free_memory(resized); + + /* NULL input pointer and size zero returns NULL */ +#ifdef NDEBUG + resized = H5resize_memory(NULL, 0); + CHECK_PTR_NULL(resized, "H5resize_memory"); /*BAD*/ +#endif /* NDEBUG */ + +} /* end test_misc32() */ + + +/**************************************************************** ** ** test_misc(): Main misc. test routine. ** @@ -5405,6 +5472,7 @@ test_misc(void) test_misc29(); /* Test that speculative metadata reads are handled correctly */ test_misc30(); /* Exercise local heap loading bug where free lists were getting dropped */ test_misc31(); /* Test Reentering library through deprecated routines after H5close() */ + test_misc32(); /* Test filter memory allocation functions */ } /* test_misc() */ |