summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--release_docs/RELEASE.txt27
-rw-r--r--src/H5.c89
-rw-r--r--src/H5MM.c121
-rw-r--r--src/H5MMprivate.h6
-rw-r--r--src/H5public.h2
-rw-r--r--test/testhdf5.h12
-rw-r--r--test/tmisc.c68
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
----------------
diff --git a/src/H5.c b/src/H5.c
index c8f7775..b73b614 100644
--- a/src/H5.c
+++ b/src/H5.c
@@ -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*/
+
diff --git a/src/H5MM.c b/src/H5MM.c
index 4f1f392..c5450fe 100644
--- a/src/H5MM.c
+++ b/src/H5MM.c
@@ -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() */