1 /*
2  * Copyright © 2011  Google, Inc.
3  *
4  *  This is part of HarfBuzz, a text shaping library.
5  *
6  * Permission is hereby granted, without written agreement and without
7  * license or royalty fees, to use, copy, modify, and distribute this
8  * software and its documentation for any purpose, provided that the
9  * above copyright notice and the following two paragraphs appear in
10  * all copies of this software.
11  *
12  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16  * DAMAGE.
17  *
18  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23  *
24  * Google Author(s): Behdad Esfahbod
25  */
26 
27 #include "hb-test.h"
28 
29 /* Unit tests for hb-blob.h */
30 
31 #if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT) && defined(HAVE_MMAP)
32 
33 # define TEST_MMAP 1
34 
35 #ifdef HAVE_SYS_MMAN_H
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif /* HAVE_UNISTD_H */
39 #include <sys/mman.h>
40 #endif /* HAVE_SYS_MMAN_H */
41 
42 #endif
43 
44 
45 static void
test_blob_empty(void)46 test_blob_empty (void)
47 {
48   hb_blob_t *blob;
49   unsigned int len;
50   const char *data;
51   char *data_writable;
52 
53   g_assert (hb_blob_is_immutable (hb_blob_get_empty ()));
54   g_assert (hb_blob_get_empty () != NULL);
55   g_assert (hb_blob_get_empty () == hb_blob_create (NULL, 0, HB_MEMORY_MODE_READONLY, NULL, NULL));
56   g_assert (hb_blob_get_empty () == hb_blob_create ("asdf", 0, HB_MEMORY_MODE_READONLY, NULL, NULL));
57   g_assert (hb_blob_get_empty () == hb_blob_create (NULL, -1, HB_MEMORY_MODE_READONLY, NULL, NULL));
58   g_assert (hb_blob_get_empty () == hb_blob_create ("asdfg", -1, HB_MEMORY_MODE_READONLY, NULL, NULL));
59 
60   blob = hb_blob_get_empty ();
61   g_assert (blob == hb_blob_get_empty ());
62 
63   len = hb_blob_get_length (blob);
64   g_assert_cmpint (len, ==, 0);
65 
66   data = hb_blob_get_data (blob, NULL);
67   g_assert (data == NULL);
68 
69   data = hb_blob_get_data (blob, &len);
70   g_assert (data == NULL);
71   g_assert_cmpint (len, ==, 0);
72 
73   data_writable = hb_blob_get_data_writable (blob, NULL);
74   g_assert (data_writable == NULL);
75 
76   data_writable = hb_blob_get_data_writable (blob, &len);
77   g_assert (data_writable == NULL);
78   g_assert_cmpint (len, ==, 0);
79 }
80 
81 static const char test_data[] = "test\0data";
82 
83 static const char *blob_names[] = {
84   "duplicate",
85   "readonly",
86   "writable"
87 #ifdef TEST_MMAP
88    , "readonly-may-make-writable"
89 #endif
90 };
91 
92 typedef struct
93 {
94   hb_blob_t *blob;
95   int freed;
96   char *data;
97   unsigned int len;
98 } fixture_t;
99 
100 static void
free_up(fixture_t * fixture)101 free_up (fixture_t *fixture)
102 {
103   g_assert_cmpint (fixture->freed, ==, 0);
104   fixture->freed++;
105 }
106 
107 static void
free_up_free(fixture_t * fixture)108 free_up_free (fixture_t *fixture)
109 {
110   free_up (fixture);
111   free (fixture->data);
112 }
113 
114 
115 #ifdef TEST_MMAP
116 static uintptr_t
get_pagesize(void)117 get_pagesize (void)
118 {
119   uintptr_t pagesize = (uintptr_t) -1;
120 
121 #if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE)
122   pagesize = (uintptr_t) sysconf (_SC_PAGE_SIZE);
123 #elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
124   pagesize = (uintptr_t) sysconf (_SC_PAGESIZE);
125 #elif defined(HAVE_GETPAGESIZE)
126   pagesize = (uintptr_t) getpagesize ();
127 #endif
128 
129   g_assert (pagesize != (uintptr_t) -1);
130 
131   return pagesize;
132 }
133 
134 static void
free_up_munmap(fixture_t * fixture)135 free_up_munmap (fixture_t *fixture)
136 {
137   free_up (fixture);
138   munmap (fixture->data, get_pagesize ());
139 }
140 #endif
141 
142 #include <errno.h>
143 static void
fixture_init(fixture_t * fixture,gconstpointer user_data)144 fixture_init (fixture_t *fixture, gconstpointer user_data)
145 {
146   hb_memory_mode_t mm = (hb_memory_mode_t) GPOINTER_TO_INT (user_data);
147   unsigned int len;
148   const char *data;
149   hb_destroy_func_t free_func;
150 
151   switch (GPOINTER_TO_INT (user_data))
152   {
153     case HB_MEMORY_MODE_DUPLICATE:
154       data = test_data;
155       len = sizeof (test_data);
156       free_func = (hb_destroy_func_t) free_up;
157       break;
158 
159     case HB_MEMORY_MODE_READONLY:
160       data = test_data;
161       len = sizeof (test_data);
162       free_func = (hb_destroy_func_t) free_up;
163       break;
164 
165     case HB_MEMORY_MODE_WRITABLE:
166       data = malloc (sizeof (test_data));
167       memcpy ((char *) data, test_data, sizeof (test_data));
168       len = sizeof (test_data);
169       free_func = (hb_destroy_func_t) free_up_free;
170       break;
171 
172 #ifdef TEST_MMAP
173     case HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE:
174     {
175       uintptr_t pagesize = get_pagesize ();
176 
177       data = mmap (NULL, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
178       g_assert (data != (char *) -1);
179       memcpy ((char *) data, test_data, sizeof (test_data));
180       mprotect ((char *) data, pagesize, PROT_READ);
181       len = sizeof (test_data);
182       free_func = (hb_destroy_func_t) free_up_munmap;
183       break;
184     }
185 #endif
186 
187     default:
188       g_assert_not_reached ();
189   }
190 
191   fixture->freed = 0;
192   fixture->data = (char *) data;
193   fixture->len = len;
194   fixture->blob = hb_blob_create (data, len, mm, fixture, free_func);
195 }
196 
197 static void
fixture_finish(fixture_t * fixture,gconstpointer user_data HB_UNUSED)198 fixture_finish (fixture_t *fixture, gconstpointer user_data HB_UNUSED)
199 {
200   hb_blob_destroy (fixture->blob);
201   g_assert_cmpint (fixture->freed, ==, 1);
202 }
203 
204 
205 static void
test_blob(fixture_t * fixture,gconstpointer user_data)206 test_blob (fixture_t *fixture, gconstpointer user_data)
207 {
208   hb_blob_t *b = fixture->blob;
209   hb_memory_mode_t mm = GPOINTER_TO_INT (user_data);
210   unsigned int len;
211   const char *data;
212   char *data_writable;
213   unsigned int i;
214 
215   g_assert (b);
216 
217   len = hb_blob_get_length (b);
218   g_assert_cmpint (len, ==, fixture->len);
219 
220   data = hb_blob_get_data (b, &len);
221   g_assert_cmpint (len, ==, fixture->len);
222   if (mm == HB_MEMORY_MODE_DUPLICATE) {
223     g_assert (data != fixture->data);
224     g_assert_cmpint (fixture->freed, ==, 1);
225     mm = HB_MEMORY_MODE_WRITABLE;
226   } else {
227     g_assert (data == fixture->data);
228     g_assert_cmpint (fixture->freed, ==, 0);
229   }
230 
231   data_writable = hb_blob_get_data_writable (b, &len);
232   g_assert_cmpint (len, ==, fixture->len);
233   g_assert (data_writable);
234   g_assert (0 == memcmp (data_writable, fixture->data, fixture->len));
235   if (mm == HB_MEMORY_MODE_READONLY) {
236     g_assert (data_writable != data);
237     g_assert_cmpint (fixture->freed, ==, 1);
238   } else {
239     g_assert (data_writable == data);
240   }
241 
242   data = hb_blob_get_data (b, &len);
243   g_assert_cmpint (len, ==, fixture->len);
244   g_assert (data == data_writable);
245 
246   memset (data_writable, 0, fixture->len);
247 
248   /* Now, make it immutable and watch get_data_writable() fail */
249 
250   g_assert (!hb_blob_is_immutable (b));
251   hb_blob_make_immutable (b);
252   g_assert (hb_blob_is_immutable (b));
253 
254   data_writable = hb_blob_get_data_writable (b, &len);
255   g_assert (!data_writable);
256   g_assert_cmpint (len, ==, 0);
257 
258   data = hb_blob_get_data (b, &len);
259   g_assert_cmpint (len, ==, fixture->len);
260   for (i = 0; i < len; i++)
261     g_assert ('\0' == data[i]);
262 }
263 
264 static void
test_blob_subblob(fixture_t * fixture,gconstpointer user_data)265 test_blob_subblob (fixture_t *fixture, gconstpointer user_data)
266 {
267   hb_blob_t *b = fixture->blob;
268   hb_memory_mode_t mm = GPOINTER_TO_INT (user_data);
269   unsigned int len;
270   const char *data;
271   char *data_writable;
272   unsigned int i;
273 
274   if (mm == HB_MEMORY_MODE_DUPLICATE) {
275     g_assert_cmpint (fixture->freed, ==, 1);
276     fixture->data = (char *) hb_blob_get_data (b, NULL);
277   } else {
278     g_assert_cmpint (fixture->freed, ==, 0);
279   }
280   fixture->blob = hb_blob_create_sub_blob (b, 1, fixture->len - 2);
281   hb_blob_destroy (b);
282   b = fixture->blob;
283 
284   /* A sub-blob is always created READONLY. */
285 
286   g_assert (b);
287 
288   len = hb_blob_get_length (b);
289   g_assert_cmpint (len, ==, fixture->len - 2);
290 
291   data = hb_blob_get_data (b, &len);
292   g_assert_cmpint (len, ==, fixture->len - 2);
293   g_assert (data == fixture->data + 1);
294 
295   data_writable = hb_blob_get_data_writable (b, &len);
296   g_assert_cmpint (len, ==, fixture->len - 2);
297   g_assert (data_writable);
298   if (mm == HB_MEMORY_MODE_READONLY)
299     g_assert (0 == memcmp (data_writable, fixture->data + 1, fixture->len - 2));
300   g_assert (data_writable != data);
301   g_assert_cmpint (fixture->freed, ==, 1);
302 
303   data = hb_blob_get_data (b, &len);
304   g_assert_cmpint (len, ==, fixture->len - 2);
305   g_assert (data == data_writable);
306 
307   memset (data_writable, 0, fixture->len - 2);
308 
309   /* Now, make it immutable and watch get_data_writable() fail */
310 
311   g_assert (!hb_blob_is_immutable (b));
312   hb_blob_make_immutable (b);
313   g_assert (hb_blob_is_immutable (b));
314 
315   data_writable = hb_blob_get_data_writable (b, &len);
316   g_assert (!data_writable);
317   g_assert_cmpint (len, ==, 0);
318 
319   data = hb_blob_get_data (b, &len);
320   g_assert_cmpint (len, ==, fixture->len - 2);
321   for (i = 0; i < len; i++)
322     g_assert ('\0' == data[i]);
323 }
324 
325 
326 int
main(int argc,char ** argv)327 main (int argc, char **argv)
328 {
329   unsigned int i;
330 
331   hb_test_init (&argc, &argv);
332 
333   hb_test_add (test_blob_empty);
334 
335   for (i = 0; i < G_N_ELEMENTS (blob_names); i++)
336   {
337     const void *blob_type = GINT_TO_POINTER (i);
338     const char *blob_name = blob_names[i];
339 
340     hb_test_add_fixture_flavor (fixture, blob_type, blob_name, test_blob);
341     hb_test_add_fixture_flavor (fixture, blob_type, blob_name, test_blob_subblob);
342   }
343 
344   /*
345    * create_sub_blob
346    */
347 
348   return hb_test_run ();
349 }
350