• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 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
addHeapMutexTest(TestNode ** root)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) UPRV_BLOCK_MACRO_BEGIN { \
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 } UPRV_BLOCK_MACRO_END
51 
52 
53 #define TEST_ASSERT(expr) UPRV_BLOCK_MACRO_BEGIN { \
54     if (!(expr)) { \
55         log_err("FAILED Assertion \"" #expr "\" at  %s:%d.\n", __FILE__, __LINE__); \
56         gMutexFailures++; \
57     } \
58 } UPRV_BLOCK_MACRO_END
59 
60 
61 /*  These tests do cleanup and reinitialize ICU in the course of their operation.
62  *    The ICU data directory must be preserved across these operations.
63  *    Here is a helper function to assist with that.
64  */
safeGetICUDataDirectory()65 static char *safeGetICUDataDirectory() {
66     const char *dataDir = u_getDataDirectory();  /* Returned string vanashes with u_cleanup */
67     char *retStr = NULL;
68     if (dataDir != NULL) {
69         retStr = (char *)malloc(strlen(dataDir)+1);
70         strcpy(retStr, dataDir);
71     }
72     return retStr;
73 }
74 
75 
76 
77 /*
78  *  Test Heap Functions.
79  *    Implemented on top of the standard malloc heap.
80  *    All blocks increased in size by 8 to 16 bytes, and the pointer returned to ICU is
81  *       offset up by 8 to 16, which should cause a good heap corruption if one of our "blocks"
82  *       ends up being freed directly, without coming through us.
83  *    Allocations are counted, to check that ICU actually does call back to us.
84  */
85 int    gBlockCount = 0;
86 const void  *gContext;
87 
myMemAlloc(const void * context,size_t size)88 static void * U_CALLCONV myMemAlloc(const void *context, size_t size) {
89     (void)context; // suppress compiler warnings about unused variable
90     char *retPtr = (char *)malloc(size+sizeof(ctest_AlignedMemory));
91     if (retPtr != NULL) {
92         retPtr += sizeof(ctest_AlignedMemory);
93     }
94     gBlockCount ++;
95     return retPtr;
96 }
97 
myMemFree(const void * context,void * mem)98 static void U_CALLCONV myMemFree(const void *context, void *mem) {
99     (void)context; // suppress compiler warnings about unused variable
100     char *freePtr = (char *)mem;
101     if (freePtr != NULL) {
102         freePtr -= sizeof(ctest_AlignedMemory);
103     }
104     free(freePtr);
105 }
106 
107 
108 
myMemRealloc(const void * context,void * mem,size_t size)109 static void * U_CALLCONV myMemRealloc(const void *context, void *mem, size_t size) {
110     (void)context; // suppress compiler warnings about unused variable
111     char *p = (char *)mem;
112     char *retPtr;
113 
114     if (p!=NULL) {
115         p -= sizeof(ctest_AlignedMemory);
116     }
117     retPtr = realloc(p, size+sizeof(ctest_AlignedMemory));
118     if (retPtr != NULL) {
119         p += sizeof(ctest_AlignedMemory);
120     }
121     return retPtr;
122 }
123 
124 
TestHeapFunctions()125 static void TestHeapFunctions() {
126     UErrorCode       status = U_ZERO_ERROR;
127     UResourceBundle *rb     = NULL;
128     char            *icuDataDir;
129     UVersionInfo unicodeVersion = {0,0,0,0};
130 
131     icuDataDir = safeGetICUDataDirectory();   /* save icu data dir, so we can put it back
132                                                *  after doing u_cleanup().                */
133 
134 
135     /* Verify that ICU can be cleaned up and reinitialized successfully.
136      *  Failure here usually means that some ICU service didn't clean up successfully,
137      *  probably because some earlier test accidentally left something open. */
138     ctest_resetICU();
139 
140     /* Un-initialize ICU */
141     u_cleanup();
142 
143     /* Can not set memory functions with NULL values */
144     status = U_ZERO_ERROR;
145     u_setMemoryFunctions(&gContext, NULL, myMemRealloc, myMemFree, &status);
146     TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
147     status = U_ZERO_ERROR;
148     u_setMemoryFunctions(&gContext, myMemAlloc, NULL, myMemFree, &status);
149     TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
150     status = U_ZERO_ERROR;
151     u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, NULL, &status);
152     TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
153 
154     /* u_setMemoryFunctions() should work with null or non-null context pointer */
155     status = U_ZERO_ERROR;
156     u_setMemoryFunctions(NULL, myMemAlloc, myMemRealloc, myMemFree, &status);
157     TEST_STATUS(status, U_ZERO_ERROR);
158     u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, myMemFree, &status);
159     TEST_STATUS(status, U_ZERO_ERROR);
160 
161 
162     /* After reinitializing ICU, we can not set the memory funcs again. */
163     status = U_ZERO_ERROR;
164     u_setDataDirectory(icuDataDir);
165     u_init(&status);
166     TEST_STATUS(status, U_ZERO_ERROR);
167 
168     /* Doing ICU operations should cause allocations to come through our test heap */
169     gBlockCount = 0;
170     status = U_ZERO_ERROR;
171     rb = ures_open(NULL, "es", &status);
172     TEST_STATUS(status, U_ZERO_ERROR);
173     if (gBlockCount == 0) {
174         log_err("Heap functions are not being called from ICU.\n");
175     }
176     ures_close(rb);
177 
178     /* Cleanup should put the heap back to its default implementation. */
179     ctest_resetICU();
180     u_getUnicodeVersion(unicodeVersion);
181     if (unicodeVersion[0] <= 0) {
182         log_err("Properties doesn't reinitialize without u_init.\n");
183     }
184     status = U_ZERO_ERROR;
185     u_init(&status);
186     TEST_STATUS(status, U_ZERO_ERROR);
187 
188     /* ICU operations should no longer cause allocations to come through our test heap */
189     gBlockCount = 0;
190     status = U_ZERO_ERROR;
191     rb = ures_open(NULL, "fr", &status);
192     TEST_STATUS(status, U_ZERO_ERROR);
193     if (gBlockCount != 0) {
194         log_err("Heap functions did not reset after u_cleanup.\n");
195     }
196     ures_close(rb);
197     free(icuDataDir);
198 
199     ctest_resetICU();
200 }
201 
202 
203