xref: /php-src/ext/dom/lexbor/lexbor/core/mraw.c (revision bffab33a)
1 /*
2  * Copyright (C) 2018-2019 Alexander Borisov
3  *
4  * Author: Alexander Borisov <borisov@lexbor.com>
5  */
6 
7 #include "lexbor/core/mraw.h"
8 
9 
10 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
11     #include <sanitizer/asan_interface.h>
12 #endif
13 
14 
15 #define lexbor_mraw_meta_set(data, size)                                       \
16     do {                                                                       \
17         memcpy(data, size, sizeof(size_t));                                    \
18     }                                                                          \
19     while (0)
20 
21 #define lexbor_mraw_data_begin(data)                                           \
22     &((uint8_t *) (data))[ lexbor_mraw_meta_size() ]
23 
24 
25 lxb_inline void *
26 lexbor_mraw_realloc_tail(lexbor_mraw_t *mraw, void *data, void *begin,
27                          size_t size, size_t begin_len, size_t new_size,
28                          bool *is_valid);
29 
30 
31 lexbor_mraw_t *
lexbor_mraw_create(void)32 lexbor_mraw_create(void)
33 {
34     return lexbor_calloc(1, sizeof(lexbor_mraw_t));
35 }
36 
37 lxb_status_t
lexbor_mraw_init(lexbor_mraw_t * mraw,size_t chunk_size)38 lexbor_mraw_init(lexbor_mraw_t *mraw, size_t chunk_size)
39 {
40     lxb_status_t status;
41 
42     if (mraw == NULL) {
43         return LXB_STATUS_ERROR_OBJECT_IS_NULL;
44     }
45 
46     if (chunk_size == 0) {
47         return LXB_STATUS_ERROR_WRONG_ARGS;
48     }
49 
50     /* Init memory */
51     mraw->mem = lexbor_mem_create();
52 
53     status = lexbor_mem_init(mraw->mem, chunk_size + lexbor_mraw_meta_size());
54     if (status) {
55         return status;
56     }
57 
58 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
59     ASAN_POISON_MEMORY_REGION(mraw->mem->chunk->data, mraw->mem->chunk->size);
60 #endif
61 
62     /* Cache */
63     mraw->cache = lexbor_bst_create();
64 
65     status = lexbor_bst_init(mraw->cache, 512);
66     if (status) {
67         return status;
68     }
69 
70     mraw->ref_count = 0;
71 
72     return LXB_STATUS_OK;
73 }
74 
75 void
lexbor_mraw_clean(lexbor_mraw_t * mraw)76 lexbor_mraw_clean(lexbor_mraw_t *mraw)
77 {
78     if (mraw != NULL) {
79         lexbor_mem_clean(mraw->mem);
80         lexbor_bst_clean(mraw->cache);
81 
82         mraw->ref_count = 0;
83     }
84 }
85 
86 lexbor_mraw_t *
lexbor_mraw_destroy(lexbor_mraw_t * mraw,bool destroy_self)87 lexbor_mraw_destroy(lexbor_mraw_t *mraw, bool destroy_self)
88 {
89     if (mraw == NULL) {
90         return NULL;
91     }
92 
93     mraw->mem = lexbor_mem_destroy(mraw->mem, true);
94     mraw->cache = lexbor_bst_destroy(mraw->cache, true);
95 
96     if (destroy_self) {
97         return lexbor_free(mraw);
98     }
99 
100     return mraw;
101 }
102 
103 lxb_inline void *
lexbor_mraw_mem_alloc(lexbor_mraw_t * mraw,size_t length)104 lexbor_mraw_mem_alloc(lexbor_mraw_t *mraw, size_t length)
105 {
106     uint8_t *data;
107     lexbor_mem_t *mem = mraw->mem;
108 
109     if (length == 0) {
110         return NULL;
111     }
112 
113     if ((mem->chunk->length + length) > mem->chunk->size) {
114         lexbor_mem_chunk_t *chunk = mem->chunk;
115 
116         if ((SIZE_MAX - mem->chunk_length) == 0) {
117             return NULL;
118         }
119 
120         if (chunk->length == 0) {
121             lexbor_mem_chunk_destroy(mem, chunk, false);
122             lexbor_mem_chunk_init(mem, chunk, length);
123 
124             chunk->length = length;
125 
126 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
127             ASAN_POISON_MEMORY_REGION(chunk->data, chunk->size);
128 #endif
129 
130             return chunk->data;
131         }
132 
133         size_t diff = lexbor_mem_align_floor(chunk->size - chunk->length);
134 
135         /* Save tail to cache */
136         if (diff > lexbor_mraw_meta_size()) {
137             diff -= lexbor_mraw_meta_size();
138 
139 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
140             ASAN_UNPOISON_MEMORY_REGION(&chunk->data[chunk->length],
141                                         lexbor_mraw_meta_size());
142 #endif
143 
144             lexbor_mraw_meta_set(&chunk->data[chunk->length], &diff);
145 
146 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
147             ASAN_POISON_MEMORY_REGION(&chunk->data[chunk->length],
148                                       diff + lexbor_mraw_meta_size());
149 #endif
150 
151             lexbor_bst_insert(mraw->cache,
152                               lexbor_bst_root_ref(mraw->cache), diff,
153                               lexbor_mraw_data_begin(&chunk->data[chunk->length]));
154 
155             chunk->length = chunk->size;
156         }
157 
158         chunk->next = lexbor_mem_chunk_make(mem, length);
159         if (chunk->next == NULL) {
160             return NULL;
161         }
162 
163         chunk->next->prev = chunk;
164         mem->chunk = chunk->next;
165 
166         mem->chunk_length++;
167 
168 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
169         ASAN_POISON_MEMORY_REGION(mem->chunk->data, mem->chunk->size);
170 #endif
171     }
172 
173     data = &mem->chunk->data[ mem->chunk->length ];
174     mem->chunk->length += length;
175 
176     return data;
177 }
178 
179 void *
lexbor_mraw_alloc(lexbor_mraw_t * mraw,size_t size)180 lexbor_mraw_alloc(lexbor_mraw_t *mraw, size_t size)
181 {
182     void *data;
183 
184     size = lexbor_mem_align(size);
185 
186     if (mraw->cache->tree_length != 0) {
187         data = lexbor_bst_remove_close(mraw->cache,
188                                        lexbor_bst_root_ref(mraw->cache),
189                                        size, NULL);
190         if (data != NULL) {
191 
192 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
193             uint8_t *real_data = ((uint8_t *) data) - lexbor_mraw_meta_size();
194 
195             /* Set unpoison for current data size */
196             ASAN_UNPOISON_MEMORY_REGION(real_data, lexbor_mraw_meta_size());
197 
198             size_t cur_size = lexbor_mraw_data_size(data);
199 
200             ASAN_UNPOISON_MEMORY_REGION(real_data,
201                                         (cur_size + lexbor_mraw_meta_size()));
202 #endif
203 
204             mraw->ref_count++;
205 
206             return data;
207         }
208     }
209 
210     data = lexbor_mraw_mem_alloc(mraw, (size + lexbor_mraw_meta_size()));
211 
212     if (data == NULL) {
213         return NULL;
214     }
215 
216 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
217     ASAN_UNPOISON_MEMORY_REGION(data, (size + lexbor_mraw_meta_size()));
218 #endif
219 
220     mraw->ref_count++;
221 
222     lexbor_mraw_meta_set(data, &size);
223     return lexbor_mraw_data_begin(data);
224 }
225 
226 void *
lexbor_mraw_calloc(lexbor_mraw_t * mraw,size_t size)227 lexbor_mraw_calloc(lexbor_mraw_t *mraw, size_t size)
228 {
229     void *data = lexbor_mraw_alloc(mraw, size);
230 
231     if (data != NULL) {
232         memset(data, 0, lexbor_mraw_data_size(data));
233     }
234 
235     return data;
236 }
237 
238 /*
239  * TODO: I don't really like this interface. Perhaps need to simplify.
240  */
241 lxb_inline void *
lexbor_mraw_realloc_tail(lexbor_mraw_t * mraw,void * data,void * begin,size_t size,size_t begin_len,size_t new_size,bool * is_valid)242 lexbor_mraw_realloc_tail(lexbor_mraw_t *mraw, void *data, void *begin,
243                          size_t size, size_t begin_len, size_t new_size,
244                          bool *is_valid)
245 {
246     lexbor_mem_chunk_t *chunk = mraw->mem->chunk;
247 
248     if (chunk->size > (begin_len + new_size)) {
249         *is_valid = true;
250 
251         if (new_size == 0) {
252             chunk->length = begin_len - lexbor_mraw_meta_size();
253             return NULL;
254         }
255 
256 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
257         ASAN_UNPOISON_MEMORY_REGION(begin, new_size + lexbor_mraw_meta_size());
258 #endif
259 
260         chunk->length = begin_len + new_size;
261         memcpy(begin, &new_size, sizeof(size_t));
262 
263         return data;
264     }
265 
266     /*
267      * If the tail is short then we increase the current data.
268      */
269     if (begin_len == lexbor_mraw_meta_size()) {
270         void *new_data;
271         lexbor_mem_chunk_t new_chunk;
272 
273         *is_valid = true;
274 
275         lexbor_mem_chunk_init(mraw->mem, &new_chunk,
276                               new_size + lexbor_mraw_meta_size());
277         if(new_chunk.data == NULL) {
278             return NULL;
279         }
280 
281         lexbor_mraw_meta_set(new_chunk.data, &new_size);
282         new_data = lexbor_mraw_data_begin(new_chunk.data);
283 
284         if (size != 0) {
285             memcpy(new_data, data, sizeof(uint8_t) * size);
286         }
287 
288 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
289         ASAN_UNPOISON_MEMORY_REGION(chunk->data, chunk->size);
290 #endif
291 
292         lexbor_mem_chunk_destroy(mraw->mem, chunk, false);
293 
294         chunk->data = new_chunk.data;
295         chunk->size = new_chunk.size;
296         chunk->length = new_size + lexbor_mraw_meta_size();
297 
298         return new_data;
299     }
300 
301     *is_valid = false;
302 
303     /*
304      * Next, this piece will go into the cache.
305      */
306     size = lexbor_mem_align_floor(size + (chunk->size - chunk->length));
307     memcpy(begin, &size, sizeof(size_t));
308 
309     chunk->length = chunk->size;
310 
311     return NULL;
312 }
313 
314 void *
lexbor_mraw_realloc(lexbor_mraw_t * mraw,void * data,size_t new_size)315 lexbor_mraw_realloc(lexbor_mraw_t *mraw, void *data, size_t new_size)
316 {
317     void *begin;
318     size_t size, begin_len;
319     lexbor_mem_chunk_t *chunk = mraw->mem->chunk;
320 
321     begin = ((uint8_t *) data) - lexbor_mraw_meta_size();
322     memcpy(&size, begin, sizeof(size_t));
323 
324     new_size = lexbor_mem_align(new_size);
325 
326     /*
327      * Look, whether there is an opportunity
328      * to prolong the current data in chunk?
329      */
330     if (chunk->length >= size) {
331         begin_len = chunk->length - size;
332 
333         if (&chunk->data[begin_len] == data) {
334             bool is_valid;
335             void *ptr = lexbor_mraw_realloc_tail(mraw, data, begin,
336                                                  size, begin_len, new_size,
337                                                  &is_valid);
338             if (is_valid == true) {
339                 return ptr;
340             }
341         }
342     }
343 
344     if (new_size < size) {
345         if (new_size == 0) {
346 
347 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
348             ASAN_POISON_MEMORY_REGION(begin, size + lexbor_mraw_meta_size());
349 #endif
350             mraw->ref_count--;
351 
352             lexbor_bst_insert(mraw->cache, lexbor_bst_root_ref(mraw->cache),
353                               size, data);
354             return NULL;
355         }
356 
357         size_t diff = lexbor_mem_align_floor(size - new_size);
358 
359         if (diff > lexbor_mraw_meta_size()) {
360             memcpy(begin, &new_size, sizeof(size_t));
361 
362             new_size = diff - lexbor_mraw_meta_size();
363             begin = &((uint8_t *) data)[diff];
364 
365             lexbor_mraw_meta_set(begin, &new_size);
366 
367 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
368             ASAN_POISON_MEMORY_REGION(begin, new_size + lexbor_mraw_meta_size());
369 #endif
370             lexbor_bst_insert(mraw->cache, lexbor_bst_root_ref(mraw->cache),
371                               new_size, lexbor_mraw_data_begin(begin));
372         }
373 
374         return data;
375     }
376 
377     begin = lexbor_mraw_alloc(mraw, new_size);
378     if (begin == NULL) {
379         return NULL;
380     }
381 
382     if (size != 0) {
383         memcpy(begin, data, sizeof(uint8_t) * size);
384     }
385 
386     lexbor_mraw_free(mraw, data);
387 
388     return begin;
389 }
390 
391 void *
lexbor_mraw_free(lexbor_mraw_t * mraw,void * data)392 lexbor_mraw_free(lexbor_mraw_t *mraw, void *data)
393 {
394     size_t size = lexbor_mraw_data_size(data);
395 
396 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
397     uint8_t *real_data = ((uint8_t *) data) - lexbor_mraw_meta_size();
398     ASAN_POISON_MEMORY_REGION(real_data, size + lexbor_mraw_meta_size());
399 #endif
400 
401     lexbor_bst_insert(mraw->cache, lexbor_bst_root_ref(mraw->cache),
402                       size, data);
403 
404     mraw->ref_count--;
405 
406     return NULL;
407 }
408 
409 /*
410  * No inline functions for ABI.
411  */
412 size_t
lexbor_mraw_data_size_noi(void * data)413 lexbor_mraw_data_size_noi(void *data)
414 {
415     return lexbor_mraw_data_size(data);
416 }
417 
418 void
lexbor_mraw_data_size_set_noi(void * data,size_t size)419 lexbor_mraw_data_size_set_noi(void *data, size_t size)
420 {
421     lexbor_mraw_data_size_set(data, size);
422 }
423 
424 void *
lexbor_mraw_dup_noi(lexbor_mraw_t * mraw,const void * src,size_t size)425 lexbor_mraw_dup_noi(lexbor_mraw_t *mraw, const void *src, size_t size)
426 {
427     return lexbor_mraw_dup(mraw, src, size);
428 }
429