From cc866bc154d761157ad0a1dbc96b20df946b8970 Mon Sep 17 00:00:00 2001 From: Allen Byrne <50328838+byrnHDF@users.noreply.github.com> Date: Thu, 12 Aug 2021 11:04:06 -0500 Subject: Rework version-check for relnum in H5checkversion function (#896) --- release_docs/RELEASE.txt | 19 ++++ src/H5.c | 228 +++++++++++++++++++++++++++---------------- test/CMakeTests.cmake | 2 +- test/testcheck_version.sh.in | 106 ++++++++++---------- 4 files changed, 216 insertions(+), 139 deletions(-) diff --git a/release_docs/RELEASE.txt b/release_docs/RELEASE.txt index 2195154..30db16a 100644 --- a/release_docs/RELEASE.txt +++ b/release_docs/RELEASE.txt @@ -262,6 +262,25 @@ New Features Library: -------- + - Change how the release part of version, in major.minor.release is checked + for compatibility + + The HDF5 library uses a function, H5check_version, to check that + the version defined in the header files, which is used to compile an + application is compatible with the version codified in the library, which + the application loads at runtime. This previously required an exact match + or the library would print a warning, dump the build settings and then + abort or continue. An environment variable controlled the logic. + + Now the function first checks that the library release version, in + major.minor.release, is not older than the version in the headers. + Secondly, if the release version is different, it checks if either + the library version or the header version is in the exception list, in + which case the release part of version, in major.minor.release, must + be exact. An environment variable still controls the logic. + + (ADB - 2021/07/27) + - H5Gcreate1() now rejects size_hint parameters larger than UINT32_MAX The size_hint value is ultimately stored in a uint32_t struct field, diff --git a/src/H5.c b/src/H5.c index 17afafe..4cabc18 100644 --- a/src/H5.c +++ b/src/H5.c @@ -63,6 +63,10 @@ hbool_t H5_PKG_INIT_VAR = FALSE; /* Library Private Variables */ /*****************************/ +/* Library incompatible release versions */ +const unsigned VERS_RELEASE_EXCEPTIONS[] = {0, 1, 2, 4}; +const unsigned VERS_RELEASE_EXCEPTIONS_SIZE = 4; + /* statically initialize block for pthread_once call used in initializing */ /* the first global mutex */ #ifdef H5_HAVE_THREADSAFE @@ -228,13 +232,13 @@ done: } /* end H5_init_library() */ /*------------------------------------------------------------------------- - * Function: H5_term_library + * Function: H5_term_library * - * Purpose: Terminate interfaces in a well-defined order due to - * dependencies among the interfaces, then terminate - * library-specific data. + * Purpose: Terminate interfaces in a well-defined order due to + * dependencies among the interfaces, then terminate + * library-specific data. * - * Return: void + * Return: void * *------------------------------------------------------------------------- */ @@ -416,22 +420,22 @@ done: } /* end H5_term_library() */ /*------------------------------------------------------------------------- - * Function: H5dont_atexit + * Function: H5dont_atexit * - * Purpose: Indicates that the library is not to clean up after itself - * when the application exits by calling exit() or returning - * from main(). This function must be called before any other - * HDF5 function or constant is used or it will have no effect. + * Purpose: Indicates that the library is not to clean up after itself + * when the application exits by calling exit() or returning + * from main(). This function must be called before any other + * HDF5 function or constant is used or it will have no effect. * - * If this function is used then certain memory buffers will not - * be de-allocated nor will open files be flushed automatically. - * The application may still call H5close() explicitly to - * accomplish these things. + * If this function is used then certain memory buffers will not + * be de-allocated nor will open files be flushed automatically. + * The application may still call H5close() explicitly to + * accomplish these things. * - * Return: Success: non-negative + * Return: Success: non-negative * - * Failure: negative if this function is called more than - * once or if it is called too late. + * Failure: negative if this function is called more than + * once or if it is called too late. * *------------------------------------------------------------------------- */ @@ -452,19 +456,19 @@ H5dont_atexit(void) } /* end H5dont_atexit() */ /*------------------------------------------------------------------------- - * Function: H5garbage_collect + * Function: H5garbage_collect * - * Purpose: Walks through all the garbage collection routines for the - * library, which are supposed to free any unused memory they have - * allocated. + * Purpose: Walks through all the garbage collection routines for the + * library, which are supposed to free any unused memory they have + * allocated. * * These should probably be registered dynamically in a linked list of * functions to call, but there aren't that many right now, so we * hard-wire them... * - * Return: Success: non-negative + * Return: Success: non-negative * - * Failure: negative + * Failure: negative * *------------------------------------------------------------------------- */ @@ -485,9 +489,9 @@ done: } /* end H5garbage_collect() */ /*------------------------------------------------------------------------- - * Function: H5set_free_list_limits + * Function: H5set_free_list_limits * - * Purpose: Sets limits on the different kinds of free lists. Setting a value + * Purpose: Sets limits on the different kinds of free lists. Setting a value * of -1 for a limit means no limit of that type. These limits are global * for the entire library. Each "global" limit only applies to free lists * of that type, so if an application sets a limit of 1 MB on each of the @@ -505,9 +509,9 @@ done: * int blk_global_lim; IN: The limit on all "block" free list memory used * int blk_list_lim; IN: The limit on memory used in each "block" free list * - * Return: Success: non-negative + * Return: Success: non-negative * - * Failure: negative + * Failure: negative * *------------------------------------------------------------------------- */ @@ -531,11 +535,11 @@ done: } /* end H5set_free_list_limits() */ /*------------------------------------------------------------------------- - * Function: H5get_free_list_sizes + * Function: H5get_free_list_sizes * - * Purpose: Gets the current size of the different kinds of free lists that - * the library uses to manage memory. The free list sizes can be set with - * H5set_free_list_limits and garbage collected with H5garbage_collect. + * Purpose: Gets the current size of the different kinds of free lists that + * the library uses to manage memory. The free list sizes can be set with + * H5set_free_list_limits and garbage collected with H5garbage_collect. * These lists are global for the entire library. * * Parameters: @@ -544,8 +548,8 @@ done: * size_t *blk_size; OUT: The current size of all "block" free list memory used * size_t *fac_size; OUT: The current size of all "factory" free list memory used * - * Return: Success: non-negative - * Failure: negative + * Return: Success: non-negative + * Failure: negative * * Programmer: Quincey Koziol * Friday, March 6, 2020 @@ -569,23 +573,23 @@ done: } /* end H5get_free_list_sizes() */ /*------------------------------------------------------------------------- - * Function: H5get_alloc_stats + * Function: H5get_alloc_stats * - * Purpose: Gets the memory allocation statistics for the library, if the - * --enable-memory-alloc-sanity-check option was given when building the + * Purpose: Gets the memory allocation statistics for the library, if the + * --enable-memory-alloc-sanity-check option was given when building the * library. Applications can check whether this option was enabled by - * detecting if the 'H5_MEMORY_ALLOC_SANITY_CHECK' macro is defined. This - * option is enabled by default for debug builds of the library and - * disabled by default for non-debug builds. If the option is not enabled, - * all the values returned with be 0. These statistics are global for the - * entire library, but don't include allocations from chunked dataset I/O - * filters or non-native VOL connectors. + * detecting if the 'H5_MEMORY_ALLOC_SANITY_CHECK' macro is defined. This + * option is enabled by default for debug builds of the library and + * disabled by default for non-debug builds. If the option is not enabled, + * all the values returned with be 0. These statistics are global for the + * entire library, but don't include allocations from chunked dataset I/O + * filters or non-native VOL connectors. * * Parameters: * H5_alloc_stats_t *stats; OUT: Memory allocation statistics * - * Return: Success: non-negative - * Failure: negative + * Return: Success: non-negative + * Failure: negative * * Programmer: Quincey Koziol * Saturday, March 7, 2020 @@ -718,12 +722,12 @@ H5__debug_mask(const char *s) #ifdef H5_HAVE_PARALLEL /*------------------------------------------------------------------------- - * Function: H5__mpi_delete_cb + * Function: H5__mpi_delete_cb * - * Purpose: Callback attribute on MPI_COMM_SELF to terminate the HDF5 + * Purpose: Callback attribute on MPI_COMM_SELF to terminate the HDF5 * library when the communicator is destroyed, i.e. on MPI_Finalize. * - * Return: MPI_SUCCESS + * Return: MPI_SUCCESS * *------------------------------------------------------------------------- */ @@ -737,18 +741,18 @@ H5__mpi_delete_cb(MPI_Comm H5_ATTR_UNUSED comm, int H5_ATTR_UNUSED keyval, void #endif /*H5_HAVE_PARALLEL*/ /*------------------------------------------------------------------------- - * Function: H5get_libversion + * Function: H5get_libversion * - * Purpose: Returns the library version numbers through arguments. MAJNUM - * will be the major revision number of the library, MINNUM the - * minor revision number, and RELNUM the release revision number. + * Purpose: Returns the library version numbers through arguments. MAJNUM + * will be the major revision number of the library, MINNUM the + * minor revision number, and RELNUM the release revision number. * - * Note: When printing an HDF5 version number it should be printed as + * Note: When printing an HDF5 version number it should be printed as * - * printf("%u.%u.%u", maj, min, rel) or - * printf("version %u.%u release %u", maj, min, rel) + * printf("%u.%u.%u", maj, min, rel) or + * printf("version %u.%u release %u", maj, min, rel) * - * Return: Non-negative on success/Negative on failure + * Return: Non-negative on success/Negative on failure * *------------------------------------------------------------------------- */ @@ -773,17 +777,20 @@ done: } /* end H5get_libversion() */ /*------------------------------------------------------------------------- - * Function: H5check_version + * Function: H5check_version * - * Purpose: Verifies that the arguments match the version numbers - * compiled into the library. This function is intended to be - * called from user to verify that the versions of header files - * compiled into the application match the version of the hdf5 - * library. + * Purpose: Verifies that the arguments match the version numbers + * compiled into the library. This function is intended to be + * called from user to verify that the versions of header files + * compiled into the application match the version of the hdf5 + * library. + * Within major.minor.release version, the expectation + * is that all release versions are compatible, exceptions to + * this rule must be added to the VERS_RELEASE_EXCEPTIONS list. * - * Return: Success: SUCCEED + * Return: Success: SUCCEED * - * Failure: abort() + * Failure: abort() * *------------------------------------------------------------------------- */ @@ -796,6 +803,15 @@ done: "linked with a different version of static or shared HDF5 library.\n" \ "You should recompile the application or check your shared library related\n" \ "settings such as 'LD_LIBRARY_PATH'.\n" +#define RELEASE_MISMATCH_WARNING \ + "Warning! ***HDF5 library release mismatched error***\n" \ + "The HDF5 header files used to compile this application are not compatible with\n" \ + "the version used by the HDF5 library to which this application is linked.\n" \ + "Data corruption or segmentation faults may occur if the application continues.\n" \ + "This can happen when an application was compiled by one version of HDF5 but\n" \ + "linked with an incompatible version of static or shared HDF5 library.\n" \ + "You should recompile the application or check your shared library related\n" \ + "settings such as 'LD_LIBRARY_PATH'.\n" herr_t H5check_version(unsigned majnum, unsigned minnum, unsigned relnum) @@ -824,7 +840,8 @@ H5check_version(unsigned majnum, unsigned minnum, unsigned relnum) disable_version_check = (unsigned int)HDstrtol(s, NULL, 0); } - if (H5_VERS_MAJOR != majnum || H5_VERS_MINOR != minnum || H5_VERS_RELEASE != relnum) { + /* H5_VERS_MAJOR and H5_VERS_MINOR must match */ + if (H5_VERS_MAJOR != majnum || H5_VERS_MINOR != minnum || H5_VERS_RELEASE > relnum) { switch (disable_version_check) { case 0: HDfprintf(stderr, "%s%s", version_mismatch_warning, @@ -859,7 +876,51 @@ H5check_version(unsigned majnum, unsigned minnum, unsigned relnum) break; } /* end switch */ - } /* end if */ + } /* end if (H5_VERS_MAJOR != majnum || H5_VERS_MINOR != minnum || H5_VERS_RELEASE > relnum) */ + + /* H5_VERS_RELEASE should be compatible, we will only add checks for exceptions */ + if (H5_VERS_RELEASE != relnum) { + for (unsigned i = 0; i < VERS_RELEASE_EXCEPTIONS_SIZE; i++) { + /* Check for incompatible headers or incompatible library */ + if (VERS_RELEASE_EXCEPTIONS[i] == relnum || VERS_RELEASE_EXCEPTIONS[i] == H5_VERS_RELEASE) { + switch (disable_version_check) { + case 0: + HDfprintf( + stderr, "%s%s", version_mismatch_warning, + "You can, at your own risk, disable this warning by setting the environment\n" + "variable 'HDF5_DISABLE_VERSION_CHECK' to a value of '1'.\n" + "Setting it to 2 or higher will suppress the warning messages totally.\n"); + /* Mention the versions we are referring to */ + HDfprintf(stderr, "Headers are %u.%u.%u, library is %u.%u.%u\n", majnum, minnum, + relnum, (unsigned)H5_VERS_MAJOR, (unsigned)H5_VERS_MINOR, + (unsigned)H5_VERS_RELEASE); + + /* Bail out now. */ + HDfputs("Bye...\n", stderr); + HDabort(); + case 1: + /* continue with a warning */ + /* Note that the warning message is embedded in the format string.*/ + HDfprintf(stderr, + "%s'HDF5_DISABLE_VERSION_CHECK' " + "environment variable is set to %d, application will\n" + "continue at your own risk.\n", + version_mismatch_warning, disable_version_check); + /* Mention the versions we are referring to */ + HDfprintf(stderr, "Headers are %u.%u.%u, library is %u.%u.%u\n", majnum, minnum, + relnum, (unsigned)H5_VERS_MAJOR, (unsigned)H5_VERS_MINOR, + (unsigned)H5_VERS_RELEASE); + break; + default: + /* 2 or higher: continue silently */ + break; + } /* end switch */ + + } /* end if */ + + } /* end for */ + + } /* end if (H5_VERS_RELEASE != relnum) */ /* Indicate that the version check has been performed */ checked = 1; @@ -870,12 +931,9 @@ H5check_version(unsigned majnum, unsigned minnum, unsigned relnum) * Check only the first sizeof(lib_str) char. Assume the information * will fit within this size or enough significance. */ - HDsnprintf(lib_str, sizeof(lib_str), "HDF5 library version: %d.%d.%d", H5_VERS_MAJOR, H5_VERS_MINOR, - H5_VERS_RELEASE); - if (*substr) { - HDstrncat(lib_str, "-", (size_t)1); - HDstrncat(lib_str, substr, (sizeof(lib_str) - HDstrlen(lib_str)) - 1); - } /* end if */ + HDsnprintf(lib_str, sizeof(lib_str), "HDF5 library version: %d.%d.%d%s%s", H5_VERS_MAJOR, + H5_VERS_MINOR, H5_VERS_RELEASE, (*substr ? "-" : ""), substr); + if (HDstrcmp(lib_str, H5_lib_vers_info_g) != 0) { HDfputs("Warning! Library version information error.\n" "The HDF5 library version information are not " @@ -904,7 +962,7 @@ done: * is failing inexplicably, then try calling this function * first. * - * Return: Non-negative on success/Negative on failure + * Return: Non-negative on success/Negative on failure * *------------------------------------------------------------------------- */ @@ -923,11 +981,11 @@ done: } /* end H5open() */ /*------------------------------------------------------------------------- - * Function: H5close + * Function: H5close * - * Purpose: Terminate the library and release all resources. + * Purpose: Terminate the library and release all resources. * - * Return: Non-negative on success/Negative on failure + * Return: Non-negative on success/Negative on failure * *------------------------------------------------------------------------- */ @@ -948,9 +1006,9 @@ H5close(void) } /* end H5close() */ /*------------------------------------------------------------------------- - * Function: H5allocate_memory + * Function: H5allocate_memory * - * Purpose: Allocate a memory buffer with the semantics of malloc(). + * 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 @@ -988,9 +1046,9 @@ H5allocate_memory(size_t size, hbool_t clear) } /* end H5allocate_memory() */ /*------------------------------------------------------------------------- - * Function: H5resize_memory + * Function: H5resize_memory * - * Purpose: Resize a memory buffer with the semantics of realloc(). + * 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 @@ -1025,14 +1083,14 @@ H5resize_memory(void *mem, size_t size) } /* end H5resize_memory() */ /*------------------------------------------------------------------------- - * Function: H5free_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 * *------------------------------------------------------------------------- */ @@ -1049,12 +1107,12 @@ H5free_memory(void *mem) } /* end H5free_memory() */ /*------------------------------------------------------------------------- - * Function: H5is_library_threadsafe + * Function: H5is_library_threadsafe * - * Purpose: Checks to see if the library was built with thread-safety + * Purpose: Checks to see if the library was built with thread-safety * enabled. * - * Return: SUCCEED/FAIL + * Return: SUCCEED/FAIL * *------------------------------------------------------------------------- */ diff --git a/test/CMakeTests.cmake b/test/CMakeTests.cmake index 4cf11f5..2a8e425 100644 --- a/test/CMakeTests.cmake +++ b/test/CMakeTests.cmake @@ -704,10 +704,10 @@ set_tests_properties (H5TEST-tcheck_version-minor PROPERTIES WORKING_DIRECTORY ${HDF5_TEST_BINARY_DIR}/H5TEST WILL_FAIL "true" ) +# release + 1 should pass add_test (NAME H5TEST-tcheck_version-release COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ "-tr") set_tests_properties (H5TEST-tcheck_version-release PROPERTIES WORKING_DIRECTORY ${HDF5_TEST_BINARY_DIR}/H5TEST - WILL_FAIL "true" ) ############################################################################## diff --git a/test/testcheck_version.sh.in b/test/testcheck_version.sh.in index 43d1b46..6378ee5 100644 --- a/test/testcheck_version.sh.in +++ b/test/testcheck_version.sh.in @@ -15,7 +15,7 @@ # Tests for the H5check_version function. # # Programmer: Albert Cheng -# Sep 28, 2009 +# Sep 28, 2009 srcdir=@srcdir@ @@ -24,7 +24,7 @@ srcdir=@srcdir@ Shared_Lib=@enable_shared@ Static_Lib=@enable_static@ Static_exec=@STATIC_EXEC@ -h5haveexitcode=yes # default is yes +h5haveexitcode=yes # default is yes CMP='cmp -s' DIFF='diff -c' @@ -103,10 +103,10 @@ WarnMesg2(){ # mismatch). # # Expected results: -# Value of $HDF5_DISABLE_VERSION_CHECK -# unset "" -1 0 1 2 3 -# Matched OK OK OK OK OK OK OK -# Mismatched W/A W/A W/A W/A W2/OK OK W2/OK +# Value of $HDF5_DISABLE_VERSION_CHECK +# unset "" -1 0 1 2 3 +# Matched OK OK OK OK OK OK OK +# Mismatched W/A W/A W/A W/A W2/OK OK W2/OK # Result codes: # OK: No warning, exit 0. # W/A: Warning, abort and exit non-0. @@ -130,42 +130,42 @@ TESTING() { xxh5versrelease=$h5versrelease if [ "$h5DisableVersion" = unset ]; then - envcmd="" # noop + envcmd="" # noop else - envcmd="env HDF5_DISABLE_VERSION_CHECK=$h5DisableVersion" + envcmd="env HDF5_DISABLE_VERSION_CHECK=$h5DisableVersion" fi if [ "$wrongversionnumbers" = none ]; then - # OK: No warning, exit 0 - cp /dev/null $expect - expect_code=0 + # OK: No warning, exit 0 + cp /dev/null $expect + expect_code=0 else - arguments=-t"$wrongversionnumbers" - # calculate mismatched version numbers by listing. - case $wrongversionnumbers in - "M") xxh5versmajor=`expr $h5versmajor + 1` - ;; - "m") xxh5versminor=`expr $h5versminor + 1` - ;; - "r") xxh5versrelease=`expr $h5versrelease + 1` - ;; - esac - case "$h5DisableVersion" in - 1) - # W2/OK: Different Warning, exit 0. - WarnMesg2 > $expect - expect_code=0 - ;; - [2-9]|[1-9][0-9]*) - # OK: No warning, exit 0 - cp /dev/null $expect - expect_code=0 - ;; - *) # W/A: Warning, abort and exit non-0. - WarnMesg > $expect - expect_code=6 # Signal Abort exit code (128+6) - ;; - esac + arguments=-t"$wrongversionnumbers" + # calculate mismatched version numbers by listing. + case $wrongversionnumbers in + "M") xxh5versmajor=`expr $h5versmajor + 1` + ;; + "m") xxh5versminor=`expr $h5versminor + 1` + ;; + "r") xxh5versrelease=`expr $h5versrelease + 1` + ;; + esac + case "$h5DisableVersion" in + 1) + # W2/OK: Different Warning, exit 0. + WarnMesg2 > $expect + expect_code=0 + ;; + [2-9]|[1-9][0-9]*) + # OK: No warning, exit 0 + cp /dev/null $expect + expect_code=0 + ;; + *) # W/A: Warning, abort and exit non-0. + WarnMesg > $expect + expect_code=6 # Signal Abort exit code (128+6) + ;; + esac fi # Run test. @@ -177,23 +177,23 @@ TESTING() { cat $actual_err >> $actual if [ $h5haveexitcode = 'yes' -a \( $expect_code -ne $ret_code \) ]; then - echo "*FAILED*" - echo " Expected exit code ($expect_code) differs from actual code ($ret_code)" - nerrors="`expr $nerrors + 1`" + echo "*FAILED*" + echo " Expected exit code ($expect_code) differs from actual code ($ret_code)" + nerrors="`expr $nerrors + 1`" elif $CMP $expect $actual; then - echo " PASSED" + echo " PASSED" else - echo "*FAILED*" - echo " Expected result differs from actual result" - nerrors="`expr $nerrors + 1`" - test yes = "$verbose" && $DIFF $expect $actual |sed 's/^/ /' + echo "*FAILED*" + echo " Expected result differs from actual result" + nerrors="`expr $nerrors + 1`" + test yes = "$verbose" && $DIFF $expect $actual |sed 's/^/ /' fi # Clean up output file. # Also clean the core file generated by H5check_version's abort. if test -z "$HDF5_NOCLEANUP"; then - $RM $expect $actual $actual_err - $RM core + $RM $expect $actual $actual_err + $RM core fi } @@ -201,15 +201,15 @@ TESTING() { # Echo parameters for debugging if verbose mode is on. DEBUGPRINT() { if [ -n "$debugmode" ]; then - echo $* + echo $* fi } # MAIN Body nerrors=0 -verbose=yes # default on -debugmode= # default off +verbose=yes # default on +debugmode= # default off H5_HAVE_EMBEDDED_LIBINFO=`grep '#define H5_HAVE_EMBEDDED_LIBINFO ' ../src/H5pubconf.h` h5libsettings=../src/libhdf5.settings @@ -240,13 +240,13 @@ fi # Three Categories of tests: # Normal: where the version numbers all matched (wrong_version == none). # Mismatched version numbers (could be Major or minor version -# or release numbers or a combination of all three.) +# or release numbers or a combination of all three.) # Test all the above with different values of the environment variable, # HDF5_DISABLE_VERSION_CHECK, as unset, "", -1, 0, 1, 2, 3 for val_disable_version_check in unset "" -1 0 1 2 3; do - for wrong_version in none M m r; do - TESTING "$val_disable_version_check" "$wrong_version" + for wrong_version in none M m; do + TESTING "$val_disable_version_check" "$wrong_version" done done -- cgit v0.12