// © 2016 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html /******************************************************************** * COPYRIGHT: * Copyright (c) 2003-2015, International Business Machines Corporation and * others. All Rights Reserved. ********************************************************************/ /* * File hpmufn.c * */ #include "unicode/utypes.h" #include "unicode/putil.h" #include "unicode/uclean.h" #include "unicode/uchar.h" #include "unicode/ures.h" #include "cintltst.h" #include "unicode/utrace.h" #include #include /** * This should align the memory properly on any machine. */ typedef union { long t1; double t2; void *t3; } ctest_AlignedMemory; static void TestHeapFunctions(void); void addHeapMutexTest(TestNode **root); void addHeapMutexTest(TestNode** root) { addTest(root, &TestHeapFunctions, "hpmufn/TestHeapFunctions" ); } static int32_t gMutexFailures = 0; #define TEST_STATUS(status, expected) UPRV_BLOCK_MACRO_BEGIN { \ if (status != expected) { \ log_err_status(status, "FAIL at %s:%d. Actual status = \"%s\"; Expected status = \"%s\"\n", \ __FILE__, __LINE__, u_errorName(status), u_errorName(expected)); gMutexFailures++; \ } \ } UPRV_BLOCK_MACRO_END #define TEST_ASSERT(expr) UPRV_BLOCK_MACRO_BEGIN { \ if (!(expr)) { \ log_err("FAILED Assertion \"" #expr "\" at %s:%d.\n", __FILE__, __LINE__); \ gMutexFailures++; \ } \ } UPRV_BLOCK_MACRO_END /* These tests do cleanup and reinitialize ICU in the course of their operation. * The ICU data directory must be preserved across these operations. * Here is a helper function to assist with that. */ static char *safeGetICUDataDirectory() { const char *dataDir = u_getDataDirectory(); /* Returned string vanashes with u_cleanup */ char *retStr = NULL; if (dataDir != NULL) { retStr = (char *)malloc(strlen(dataDir)+1); strcpy(retStr, dataDir); } return retStr; } /* * Test Heap Functions. * Implemented on top of the standard malloc heap. * All blocks increased in size by 8 to 16 bytes, and the pointer returned to ICU is * offset up by 8 to 16, which should cause a good heap corruption if one of our "blocks" * ends up being freed directly, without coming through us. * Allocations are counted, to check that ICU actually does call back to us. */ int gBlockCount = 0; const void *gContext; static void * U_CALLCONV myMemAlloc(const void *context, size_t size) { (void)context; // suppress compiler warnings about unused variable char *retPtr = (char *)malloc(size+sizeof(ctest_AlignedMemory)); if (retPtr != NULL) { retPtr += sizeof(ctest_AlignedMemory); } gBlockCount ++; return retPtr; } static void U_CALLCONV myMemFree(const void *context, void *mem) { (void)context; // suppress compiler warnings about unused variable char *freePtr = (char *)mem; if (freePtr != NULL) { freePtr -= sizeof(ctest_AlignedMemory); } free(freePtr); } static void * U_CALLCONV myMemRealloc(const void *context, void *mem, size_t size) { (void)context; // suppress compiler warnings about unused variable char *p = (char *)mem; char *retPtr; if (p!=NULL) { p -= sizeof(ctest_AlignedMemory); } retPtr = realloc(p, size+sizeof(ctest_AlignedMemory)); if (retPtr != NULL) { p += sizeof(ctest_AlignedMemory); } return retPtr; } static void TestHeapFunctions() { UErrorCode status = U_ZERO_ERROR; UResourceBundle *rb = NULL; char *icuDataDir; UVersionInfo unicodeVersion = {0,0,0,0}; icuDataDir = safeGetICUDataDirectory(); /* save icu data dir, so we can put it back * after doing u_cleanup(). */ /* Verify that ICU can be cleaned up and reinitialized successfully. * Failure here usually means that some ICU service didn't clean up successfully, * probably because some earlier test accidentally left something open. */ ctest_resetICU(); /* Un-initialize ICU */ u_cleanup(); /* Can not set memory functions with NULL values */ status = U_ZERO_ERROR; u_setMemoryFunctions(&gContext, NULL, myMemRealloc, myMemFree, &status); TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); status = U_ZERO_ERROR; u_setMemoryFunctions(&gContext, myMemAlloc, NULL, myMemFree, &status); TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); status = U_ZERO_ERROR; u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, NULL, &status); TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); /* u_setMemoryFunctions() should work with null or non-null context pointer */ status = U_ZERO_ERROR; u_setMemoryFunctions(NULL, myMemAlloc, myMemRealloc, myMemFree, &status); TEST_STATUS(status, U_ZERO_ERROR); u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, myMemFree, &status); TEST_STATUS(status, U_ZERO_ERROR); /* After reinitializing ICU, we can not set the memory funcs again. */ status = U_ZERO_ERROR; u_setDataDirectory(icuDataDir); u_init(&status); TEST_STATUS(status, U_ZERO_ERROR); /* Doing ICU operations should cause allocations to come through our test heap */ gBlockCount = 0; status = U_ZERO_ERROR; rb = ures_open(NULL, "es", &status); TEST_STATUS(status, U_ZERO_ERROR); if (gBlockCount == 0) { log_err("Heap functions are not being called from ICU.\n"); } ures_close(rb); /* Cleanup should put the heap back to its default implementation. */ ctest_resetICU(); u_getUnicodeVersion(unicodeVersion); if (unicodeVersion[0] <= 0) { log_err("Properties doesn't reinitialize without u_init.\n"); } status = U_ZERO_ERROR; u_init(&status); TEST_STATUS(status, U_ZERO_ERROR); /* ICU operations should no longer cause allocations to come through our test heap */ gBlockCount = 0; status = U_ZERO_ERROR; rb = ures_open(NULL, "fr", &status); TEST_STATUS(status, U_ZERO_ERROR); if (gBlockCount != 0) { log_err("Heap functions did not reset after u_cleanup.\n"); } ures_close(rb); free(icuDataDir); ctest_resetICU(); }