1 // Copyright (C) 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /******************************************************************** 4 * COPYRIGHT: 5 * Copyright (c) 2003-2015, International Business Machines Corporation and 6 * others. All Rights Reserved. 7 ********************************************************************/ 8 /* 9 * File hpmufn.c 10 * 11 */ 12 13 #include "unicode/utypes.h" 14 #include "unicode/putil.h" 15 #include "unicode/uclean.h" 16 #include "unicode/uchar.h" 17 #include "unicode/ures.h" 18 #include "cintltst.h" 19 #include "unicode/utrace.h" 20 #include <stdlib.h> 21 #include <string.h> 22 23 /** 24 * This should align the memory properly on any machine. 25 */ 26 typedef union { 27 long t1; 28 double t2; 29 void *t3; 30 } ctest_AlignedMemory; 31 32 static void TestHeapFunctions(void); 33 34 void addHeapMutexTest(TestNode **root); 35 36 37 void 38 addHeapMutexTest(TestNode** root) 39 { 40 addTest(root, &TestHeapFunctions, "hpmufn/TestHeapFunctions" ); 41 } 42 43 static int32_t gMutexFailures = 0; 44 45 #define TEST_STATUS(status, expected) \ 46 if (status != expected) { \ 47 log_err_status(status, "FAIL at %s:%d. Actual status = \"%s\"; Expected status = \"%s\"\n", \ 48 __FILE__, __LINE__, u_errorName(status), u_errorName(expected)); gMutexFailures++; } 49 50 51 #define TEST_ASSERT(expr) \ 52 if (!(expr)) { \ 53 log_err("FAILED Assertion \"" #expr "\" at %s:%d.\n", __FILE__, __LINE__); \ 54 gMutexFailures++; \ 55 } 56 57 58 /* These tests do cleanup and reinitialize ICU in the course of their operation. 59 * The ICU data directory must be preserved across these operations. 60 * Here is a helper function to assist with that. 61 */ 62 static char *safeGetICUDataDirectory() { 63 const char *dataDir = u_getDataDirectory(); /* Returned string vanashes with u_cleanup */ 64 char *retStr = NULL; 65 if (dataDir != NULL) { 66 retStr = (char *)malloc(strlen(dataDir)+1); 67 strcpy(retStr, dataDir); 68 } 69 return retStr; 70 } 71 72 73 74 /* 75 * Test Heap Functions. 76 * Implemented on top of the standard malloc heap. 77 * All blocks increased in size by 8 to 16 bytes, and the poiner returned to ICU is 78 * offset up by 8 to 16, which should cause a good heap corruption if one of our "blocks" 79 * ends up being freed directly, without coming through us. 80 * Allocations are counted, to check that ICU actually does call back to us. 81 */ 82 int gBlockCount = 0; 83 const void *gContext; 84 85 static void * U_CALLCONV myMemAlloc(const void *context, size_t size) { 86 char *retPtr = (char *)malloc(size+sizeof(ctest_AlignedMemory)); 87 if (retPtr != NULL) { 88 retPtr += sizeof(ctest_AlignedMemory); 89 } 90 gBlockCount ++; 91 return retPtr; 92 } 93 94 static void U_CALLCONV myMemFree(const void *context, void *mem) { 95 char *freePtr = (char *)mem; 96 if (freePtr != NULL) { 97 freePtr -= sizeof(ctest_AlignedMemory); 98 } 99 free(freePtr); 100 } 101 102 103 104 static void * U_CALLCONV myMemRealloc(const void *context, void *mem, size_t size) { 105 char *p = (char *)mem; 106 char *retPtr; 107 108 if (p!=NULL) { 109 p -= sizeof(ctest_AlignedMemory); 110 } 111 retPtr = realloc(p, size+sizeof(ctest_AlignedMemory)); 112 if (retPtr != NULL) { 113 p += sizeof(ctest_AlignedMemory); 114 } 115 return retPtr; 116 } 117 118 119 static void TestHeapFunctions() { 120 UErrorCode status = U_ZERO_ERROR; 121 UResourceBundle *rb = NULL; 122 char *icuDataDir; 123 UVersionInfo unicodeVersion = {0,0,0,0}; 124 125 icuDataDir = safeGetICUDataDirectory(); /* save icu data dir, so we can put it back 126 * after doing u_cleanup(). */ 127 128 129 /* Verify that ICU can be cleaned up and reinitialized successfully. 130 * Failure here usually means that some ICU service didn't clean up successfully, 131 * probably because some earlier test accidently left something open. */ 132 ctest_resetICU(); 133 134 /* Un-initialize ICU */ 135 u_cleanup(); 136 137 /* Can not set memory functions with NULL values */ 138 status = U_ZERO_ERROR; 139 u_setMemoryFunctions(&gContext, NULL, myMemRealloc, myMemFree, &status); 140 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); 141 status = U_ZERO_ERROR; 142 u_setMemoryFunctions(&gContext, myMemAlloc, NULL, myMemFree, &status); 143 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); 144 status = U_ZERO_ERROR; 145 u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, NULL, &status); 146 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); 147 148 /* u_setMemoryFunctions() should work with null or non-null context pointer */ 149 status = U_ZERO_ERROR; 150 u_setMemoryFunctions(NULL, myMemAlloc, myMemRealloc, myMemFree, &status); 151 TEST_STATUS(status, U_ZERO_ERROR); 152 u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, myMemFree, &status); 153 TEST_STATUS(status, U_ZERO_ERROR); 154 155 156 /* After reinitializing ICU, we can not set the memory funcs again. */ 157 status = U_ZERO_ERROR; 158 u_setDataDirectory(icuDataDir); 159 u_init(&status); 160 TEST_STATUS(status, U_ZERO_ERROR); 161 162 /* Doing ICU operations should cause allocations to come through our test heap */ 163 gBlockCount = 0; 164 status = U_ZERO_ERROR; 165 rb = ures_open(NULL, "es", &status); 166 TEST_STATUS(status, U_ZERO_ERROR); 167 if (gBlockCount == 0) { 168 log_err("Heap functions are not being called from ICU.\n"); 169 } 170 ures_close(rb); 171 172 /* Cleanup should put the heap back to its default implementation. */ 173 ctest_resetICU(); 174 u_getUnicodeVersion(unicodeVersion); 175 if (unicodeVersion[0] <= 0) { 176 log_err("Properties doesn't reinitialize without u_init.\n"); 177 } 178 status = U_ZERO_ERROR; 179 u_init(&status); 180 TEST_STATUS(status, U_ZERO_ERROR); 181 182 /* ICU operations should no longer cause allocations to come through our test heap */ 183 gBlockCount = 0; 184 status = U_ZERO_ERROR; 185 rb = ures_open(NULL, "fr", &status); 186 TEST_STATUS(status, U_ZERO_ERROR); 187 if (gBlockCount != 0) { 188 log_err("Heap functions did not reset after u_cleanup.\n"); 189 } 190 ures_close(rb); 191 free(icuDataDir); 192 193 ctest_resetICU(); 194 } 195 196 197