/src/yara/libyara/arena.c
Line  | Count  | Source  | 
1  |  | /*  | 
2  |  | Copyright (c) 2020. The YARA Authors. All Rights Reserved.  | 
3  |  |  | 
4  |  | Redistribution and use in source and binary forms, with or without modification,  | 
5  |  | are permitted provided that the following conditions are met:  | 
6  |  |  | 
7  |  | 1. Redistributions of source code must retain the above copyright notice, this  | 
8  |  | list of conditions and the following disclaimer.  | 
9  |  |  | 
10  |  | 2. Redistributions in binary form must reproduce the above copyright notice,  | 
11  |  | this list of conditions and the following disclaimer in the documentation and/or  | 
12  |  | other materials provided with the distribution.  | 
13  |  |  | 
14  |  | 3. Neither the name of the copyright holder nor the names of its contributors  | 
15  |  | may be used to endorse or promote products derived from this software without  | 
16  |  | specific prior written permission.  | 
17  |  |  | 
18  |  | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND  | 
19  |  | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED  | 
20  |  | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  | 
21  |  | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR  | 
22  |  | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES  | 
23  |  | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;  | 
24  |  | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON  | 
25  |  | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT  | 
26  |  | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  | 
27  |  | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  | 
28  |  | */  | 
29  |  |  | 
30  |  | #include <assert.h>  | 
31  |  | #include <stdarg.h>  | 
32  |  | #include <stddef.h>  | 
33  |  | #include <yara/arena.h>  | 
34  |  | #include <yara/error.h>  | 
35  |  | #include <yara/mem.h>  | 
36  |  |  | 
37  |  | typedef struct YR_ARENA_FILE_HEADER YR_ARENA_FILE_HEADER;  | 
38  |  | typedef struct YR_ARENA_FILE_BUFFER YR_ARENA_FILE_BUFFER;  | 
39  |  |  | 
40  |  | #pragma pack(push)  | 
41  |  | #pragma pack(1)  | 
42  |  |  | 
43  |  | struct YR_ARENA_FILE_HEADER  | 
44  |  | { | 
45  |  |   uint8_t magic[4];  | 
46  |  |   uint8_t version;  | 
47  |  |   uint8_t num_buffers;  | 
48  |  | };  | 
49  |  |  | 
50  |  | struct YR_ARENA_FILE_BUFFER  | 
51  |  | { | 
52  |  |   uint64_t offset;  | 
53  |  |   uint32_t size;  | 
54  |  | };  | 
55  |  |  | 
56  |  | #pragma pack(pop)  | 
57  |  |  | 
58  |  | ////////////////////////////////////////////////////////////////////////////////  | 
59  |  | // Tells the arena that certain offsets within a buffer contain relocatable  | 
60  |  | // pointers. The offsets are passed as a vararg list where the end of the  | 
61  |  | // list is indicated by the special value EOL (-1), offsets in the list are  | 
62  |  | // relative to base_offset, which in turns is relative to the beginning of the  | 
63  |  | // buffer.  | 
64  |  | //  | 
65  |  | // Args:  | 
66  |  | //   arena: Pointer the arena.  | 
67  |  | //   buffer_id: Buffer number.  | 
68  |  | //   base_offset: Base offset.  | 
69  |  | //   offsets: List of offsets relative to base offset.  | 
70  |  | //  | 
71  |  | // Returns:  | 
72  |  | //   ERROR_SUCCESS  | 
73  |  | //   ERROR_INSUFFICIENT_MEMORY  | 
74  |  | //  | 
75  |  | static int _yr_arena_make_ptr_relocatable(  | 
76  |  |     YR_ARENA* arena,  | 
77  |  |     uint32_t buffer_id,  | 
78  |  |     yr_arena_off_t base_offset,  | 
79  |  |     va_list offsets)  | 
80  | 18  | { | 
81  | 18  |   size_t offset;  | 
82  |  |  | 
83  | 18  |   int result = ERROR_SUCCESS;  | 
84  |  |  | 
85  |  |   // The argument to va_arg is size_t because the offsets passed to this  | 
86  |  |   // function are obtained with offsetof().  | 
87  | 18  |   offset = va_arg(offsets, size_t);  | 
88  |  |  | 
89  | 42  |   while (offset != EOL)  | 
90  | 24  |   { | 
91  | 24  |     YR_RELOC* reloc = (YR_RELOC*) yr_malloc(sizeof(YR_RELOC));  | 
92  |  |  | 
93  | 24  |     if (reloc == NULL)  | 
94  | 0  |       return ERROR_INSUFFICIENT_MEMORY;  | 
95  |  |  | 
96  | 24  |     reloc->buffer_id = buffer_id;  | 
97  | 24  |     reloc->offset = base_offset + (yr_arena_off_t) offset;  | 
98  | 24  |     reloc->next = NULL;  | 
99  |  |  | 
100  | 24  |     if (arena->reloc_list_head == NULL)  | 
101  | 2  |       arena->reloc_list_head = reloc;  | 
102  |  |  | 
103  | 24  |     if (arena->reloc_list_tail != NULL)  | 
104  | 22  |       arena->reloc_list_tail->next = reloc;  | 
105  |  |  | 
106  | 24  |     arena->reloc_list_tail = reloc;  | 
107  | 24  |     offset = va_arg(offsets, size_t);  | 
108  | 24  |   }  | 
109  |  |  | 
110  | 18  |   return result;  | 
111  | 18  | }  | 
112  |  |  | 
113  |  | // Flags for _yr_arena_allocate_memory.  | 
114  | 26  | #define YR_ARENA_ZERO_MEMORY 1  | 
115  |  |  | 
116  |  | ////////////////////////////////////////////////////////////////////////////////  | 
117  |  | // Allocates memory in a buffer within the arena.  | 
118  |  | //  | 
119  |  | // Args:  | 
120  |  | //   arena: Pointer to the arena.  | 
121  |  | //   flags: Flags. The only supported flag so far is YR_ARENA_ZERO_MEMORY.  | 
122  |  | //   buffer_id: Buffer number.  | 
123  |  | //   size : Size of the region to be allocated.  | 
124  |  | //   [out] ref: Pointer to a YR_ARENA_REF that will be updated with the  | 
125  |  | //              reference to the newly allocated region. The pointer can be  | 
126  |  | //              NULL.  | 
127  |  | // Returns:  | 
128  |  | //   ERROR_SUCCESS  | 
129  |  | //   ERROR_INVALID_ARGUMENT  | 
130  |  | //   ERROR_INSUFFICIENT_MEMORY  | 
131  |  | //  | 
132  |  | static int _yr_arena_allocate_memory(  | 
133  |  |     YR_ARENA* arena,  | 
134  |  |     int flags,  | 
135  |  |     uint32_t buffer_id,  | 
136  |  |     size_t size,  | 
137  |  |     YR_ARENA_REF* ref)  | 
138  | 76  | { | 
139  | 76  |   if (buffer_id > arena->num_buffers)  | 
140  | 0  |     return ERROR_INVALID_ARGUMENT;  | 
141  |  |  | 
142  | 76  |   YR_ARENA_BUFFER* b = &arena->buffers[buffer_id];  | 
143  |  |  | 
144  |  |   // If the new data doesn't fit in the remaining space the buffer must be  | 
145  |  |   // re-sized. This implies moving the buffer to a different memory location  | 
146  |  |   // and adjusting the pointers listed in the relocation list.  | 
147  |  |  | 
148  | 76  |   if (b->size - b->used < size)  | 
149  | 16  |   { | 
150  | 16  |     size_t new_size = (b->size == 0) ? arena->initial_buffer_size : b->size * 2;  | 
151  |  |  | 
152  | 16  |     while (new_size < b->used + size) new_size *= 2;  | 
153  |  |  | 
154  |  |     // Make sure that buffer size if not larger than 4GB.  | 
155  | 16  |     if (new_size > 1ULL << 32)  | 
156  | 0  |       return ERROR_INSUFFICIENT_MEMORY;  | 
157  |  |  | 
158  | 16  |     uint8_t* new_data = yr_realloc(b->data, new_size);  | 
159  |  |  | 
160  | 16  |     if (new_data == NULL)  | 
161  | 0  |       return ERROR_INSUFFICIENT_MEMORY;  | 
162  |  |  | 
163  |  | // When yr_realloc uses the Windows API (HeapAlloc, HeapReAlloc) it is not  | 
164  |  | // necessary to set the memory to zero because the API is called with the  | 
165  |  | // HEAP_ZERO_MEMORY flag.  | 
166  | 16  | #if !defined(_WIN32) && !defined(__CYGWIN__)  | 
167  | 16  |     if (flags & YR_ARENA_ZERO_MEMORY)  | 
168  | 10  |       memset(new_data + b->used, 0, new_size - b->used);  | 
169  | 16  | #endif  | 
170  |  |  | 
171  |  |     // Fix pointers in the relocation list if the old buffer was not empty  | 
172  |  |     // and it was actually moved by yr_realloc.  | 
173  | 16  |     if (b->data != NULL && b->data != new_data)  | 
174  | 0  |     { | 
175  | 0  |       YR_RELOC* reloc = arena->reloc_list_head;  | 
176  |  | 
  | 
177  | 0  |       while (reloc != NULL)  | 
178  | 0  |       { | 
179  |  |         // If the reloc entry is for the same buffer that is being relocated,  | 
180  |  |         // the base pointer that we use to access the buffer must be new_data  | 
181  |  |         // as arena->buffers[reloc->buffer_id].data (which is the same than  | 
182  |  |         // b->data) can't be accessed anymore after the call to yr_realloc.  | 
183  | 0  |         uint8_t* base = buffer_id == reloc->buffer_id  | 
184  | 0  |                             ? new_data  | 
185  | 0  |                             : arena->buffers[reloc->buffer_id].data;  | 
186  |  |  | 
187  |  |         // reloc_address holds the address inside the buffer where the pointer  | 
188  |  |         // to be relocated resides.  | 
189  | 0  |         void** reloc_address = (void**) (base + reloc->offset);  | 
190  |  |  | 
191  |  |         // reloc_target is the value of the relocatable pointer.  | 
192  | 0  |         void* reloc_target = *reloc_address;  | 
193  |  | 
  | 
194  | 0  |         if ((uint8_t*) reloc_target >= b->data &&  | 
195  | 0  |             (uint8_t*) reloc_target < b->data + b->used)  | 
196  | 0  |         { | 
197  |  |           // reloc_target points to some data inside the buffer being moved, so  | 
198  |  |           // the pointer needs to be adjusted.  | 
199  | 0  |           *reloc_address = (uint8_t*) reloc_target - b->data + new_data;  | 
200  | 0  |         }  | 
201  |  | 
  | 
202  | 0  |         reloc = reloc->next;  | 
203  | 0  |       }  | 
204  | 0  |     }  | 
205  |  |  | 
206  | 16  |     b->size = new_size;  | 
207  | 16  |     b->data = new_data;  | 
208  | 16  |   }  | 
209  |  |  | 
210  | 76  |   if (ref != NULL)  | 
211  | 72  |   { | 
212  | 72  |     ref->buffer_id = buffer_id;  | 
213  | 72  |     ref->offset = (uint32_t) b->used;  | 
214  | 72  |   }  | 
215  |  |  | 
216  | 76  |   b->used += size;  | 
217  |  |  | 
218  | 76  |   return ERROR_SUCCESS;  | 
219  | 76  | }  | 
220  |  |  | 
221  |  | ////////////////////////////////////////////////////////////////////////////////  | 
222  |  | // Creates an arena with the specified number of buffers.  | 
223  |  | //  | 
224  |  | // Args:  | 
225  |  | //   num_buffers: Number of buffers.  | 
226  |  | //   initial_buffer_size: Initial size of each buffer.  | 
227  |  | //   [out] arena: Address of a YR_ARENA* pointer that will receive the address  | 
228  |  | //                of the newly created arena.  | 
229  |  | // Returns:  | 
230  |  | //   ERROR_SUCCESS  | 
231  |  | //   ERROR_INSUFFICIENT_MEMORY  | 
232  |  | //  | 
233  |  | int yr_arena_create(  | 
234  |  |     uint32_t num_buffers,  | 
235  |  |     size_t initial_buffer_size,  | 
236  |  |     YR_ARENA** arena)  | 
237  | 3.97k  | { | 
238  | 3.97k  |   YR_ARENA* new_arena = (YR_ARENA*) yr_calloc(1, sizeof(YR_ARENA));  | 
239  |  |  | 
240  | 3.97k  |   if (new_arena == NULL)  | 
241  | 0  |     return ERROR_INSUFFICIENT_MEMORY;  | 
242  |  |  | 
243  | 3.97k  |   new_arena->xrefs = 1;  | 
244  | 3.97k  |   new_arena->num_buffers = num_buffers;  | 
245  | 3.97k  |   new_arena->initial_buffer_size = initial_buffer_size;  | 
246  |  |  | 
247  | 3.97k  |   *arena = new_arena;  | 
248  |  |  | 
249  | 3.97k  |   return ERROR_SUCCESS;  | 
250  | 3.97k  | }  | 
251  |  |  | 
252  |  | void yr_arena_acquire(YR_ARENA* arena)  | 
253  | 2  | { | 
254  | 2  |   arena->xrefs++;  | 
255  | 2  | }  | 
256  |  |  | 
257  |  | ////////////////////////////////////////////////////////////////////////////////  | 
258  |  | // Releases the arena.  | 
259  |  | //  | 
260  |  | // Decrements the cross-references counter for the arena, if the number of  | 
261  |  | // cross-references reaches zero the arena is destroyed and all its resources  | 
262  |  | // are freed.  | 
263  |  | //  | 
264  |  | // Args:  | 
265  |  | //   arena :Pointer to the arena.  | 
266  |  | //  | 
267  |  | // Returns:  | 
268  |  | //   ERROR_SUCCESS  | 
269  |  | //   ERROR_INSUFFICIENT_MEMORY  | 
270  |  | //  | 
271  |  | int yr_arena_release(YR_ARENA* arena)  | 
272  | 3.97k  | { | 
273  | 3.97k  |   arena->xrefs--;  | 
274  |  |  | 
275  | 3.97k  |   if (arena->xrefs > 0)  | 
276  | 2  |     return ERROR_SUCCESS;  | 
277  |  |  | 
278  | 7.95k  |   for (uint32_t i = 0; i < arena->num_buffers; i++)  | 
279  | 3.97k  |   { | 
280  | 3.97k  |     if (arena->buffers[i].data != NULL)  | 
281  | 0  |       yr_free(arena->buffers[i].data);  | 
282  | 3.97k  |   }  | 
283  |  |  | 
284  | 3.97k  |   YR_RELOC* reloc = arena->reloc_list_head;  | 
285  |  |  | 
286  | 3.97k  |   while (reloc != NULL)  | 
287  | 0  |   { | 
288  | 0  |     YR_RELOC* next = reloc->next;  | 
289  | 0  |     yr_free(reloc);  | 
290  | 0  |     reloc = next;  | 
291  | 0  |   }  | 
292  |  |  | 
293  | 3.97k  |   yr_free(arena);  | 
294  |  |  | 
295  | 3.97k  |   return ERROR_SUCCESS;  | 
296  | 3.97k  | }  | 
297  |  |  | 
298  |  | ////////////////////////////////////////////////////////////////////////////////  | 
299  |  | // Allocates memory in a buffer within the arena.  | 
300  |  | //  | 
301  |  | // Args:  | 
302  |  | //   arena: Pointer to the arena.  | 
303  |  | //   buffer_id: Buffer number.  | 
304  |  | //   size : Size of the region to be allocated.  | 
305  |  | //   [out] offset: Pointer to a variable where the function puts the offset  | 
306  |  | //                 within the buffer of the allocated region. The pointer can  | 
307  |  | //                 be NULL.  | 
308  |  | // Returns:  | 
309  |  | //   ERROR_SUCCESS  | 
310  |  | //   ERROR_INVALID_ARGUMENT  | 
311  |  | //   ERROR_INSUFFICIENT_MEMORY  | 
312  |  | //  | 
313  |  | int yr_arena_allocate_memory(  | 
314  |  |     YR_ARENA* arena,  | 
315  |  |     uint32_t buffer_id,  | 
316  |  |     size_t size,  | 
317  |  |     YR_ARENA_REF* ref)  | 
318  | 0  | { | 
319  | 0  |   return _yr_arena_allocate_memory(arena, 0, buffer_id, size, ref);  | 
320  | 0  | }  | 
321  |  |  | 
322  |  | ////////////////////////////////////////////////////////////////////////////////  | 
323  |  | // Allocates memory in a buffer within the arena and fill it with zeroes.  | 
324  |  | //  | 
325  |  | // Args:  | 
326  |  | //   arena: Pointer to the arena.  | 
327  |  | //   buffer_id: Buffer number.  | 
328  |  | //   size : Size of the region to be allocated.  | 
329  |  | //   [out] offset: Pointer to a variable where the function puts the offset  | 
330  |  | //                 within the buffer of the allocated region. The pointer can  | 
331  |  | //                 be NULL.  | 
332  |  | // Returns:  | 
333  |  | //   ERROR_SUCCESS  | 
334  |  | //   ERROR_INVALID_ARGUMENT  | 
335  |  | //   ERROR_INSUFFICIENT_MEMORY  | 
336  |  | //  | 
337  |  | int yr_arena_allocate_zeroed_memory(  | 
338  |  |     YR_ARENA* arena,  | 
339  |  |     uint32_t buffer_id,  | 
340  |  |     size_t size,  | 
341  |  |     YR_ARENA_REF* ref)  | 
342  | 4  | { | 
343  | 4  |   return _yr_arena_allocate_memory(  | 
344  | 4  |       arena, YR_ARENA_ZERO_MEMORY, buffer_id, size, ref);  | 
345  | 4  | }  | 
346  |  |  | 
347  |  | ////////////////////////////////////////////////////////////////////////////////  | 
348  |  | // Allocates a structure within the arena. This function is similar to  | 
349  |  | // yr_arena_allocate_memory but additionally receives a variable-length  | 
350  |  | // list of offsets within the structure where pointers reside. This allows  | 
351  |  | // the arena to keep track of pointers that must be adjusted when memory  | 
352  |  | // is relocated. This is an example on how to invoke this function:  | 
353  |  | //  | 
354  |  | //  yr_arena_allocate_struct(  | 
355  |  | //        arena,  | 
356  |  | //        0,  | 
357  |  | //        sizeof(MY_STRUCTURE),  | 
358  |  | //        &ref,  | 
359  |  | //        offsetof(MY_STRUCTURE, field_1),  | 
360  |  | //        offsetof(MY_STRUCTURE, field_2),  | 
361  |  | //        ..  | 
362  |  | //        offsetof(MY_STRUCTURE, field_N),  | 
363  |  | //        EOL);  | 
364  |  | //  | 
365  |  | // Args:  | 
366  |  | //   arena: Pointer to the arena.  | 
367  |  | //   buffer_id: Buffer number.  | 
368  |  | //   size: Size of the region to be allocated.  | 
369  |  | //   [out] ref: Pointer to a reference that will point to the newly allocated  | 
370  |  | //              structure when the function returns. The pointer can be NULL  | 
371  |  | //              if you don't need the reference.  | 
372  |  | //   ...   Variable number of offsets relative to the beginning of the struct.  | 
373  |  | //         These offsets are of type size_t.  | 
374  |  | //  | 
375  |  | // Returns:  | 
376  |  | //   ERROR_SUCCESS  | 
377  |  | //   ERROR_INVALID_ARGUMENT  | 
378  |  | //   ERROR_INSUFFICIENT_MEMORY  | 
379  |  | //  | 
380  |  | int yr_arena_allocate_struct(  | 
381  |  |     YR_ARENA* arena,  | 
382  |  |     uint32_t buffer_id,  | 
383  |  |     size_t size,  | 
384  |  |     YR_ARENA_REF* ref,  | 
385  |  |     ...)  | 
386  | 6  | { | 
387  | 6  |   YR_ARENA_REF r;  | 
388  |  |  | 
389  | 6  |   int result = _yr_arena_allocate_memory(  | 
390  | 6  |       arena, YR_ARENA_ZERO_MEMORY, buffer_id, size, &r);  | 
391  |  |  | 
392  | 6  |   if (result != ERROR_SUCCESS)  | 
393  | 0  |     return result;  | 
394  |  |  | 
395  | 6  |   va_list field_offsets;  | 
396  | 6  |   va_start(field_offsets, ref);  | 
397  |  |  | 
398  | 6  |   result = _yr_arena_make_ptr_relocatable(  | 
399  | 6  |       arena, buffer_id, r.offset, field_offsets);  | 
400  |  |  | 
401  | 6  |   va_end(field_offsets);  | 
402  |  |  | 
403  | 6  |   if (result == ERROR_SUCCESS && ref != NULL)  | 
404  | 6  |   { | 
405  | 6  |     ref->buffer_id = r.buffer_id;  | 
406  | 6  |     ref->offset = r.offset;  | 
407  | 6  |   }  | 
408  |  |  | 
409  | 6  |   return result;  | 
410  | 6  | }  | 
411  |  |  | 
412  |  | void* yr_arena_get_ptr(  | 
413  |  |     YR_ARENA* arena,  | 
414  |  |     uint32_t buffer_id,  | 
415  |  |     yr_arena_off_t offset)  | 
416  | 4.03k  | { | 
417  | 4.03k  |   assert(buffer_id < arena->num_buffers);  | 
418  | 4.03k  |   assert(offset <= arena->buffers[buffer_id].used);  | 
419  |  |  | 
420  | 4.03k  |   if (arena->buffers[buffer_id].data == NULL)  | 
421  | 3.98k  |     return NULL;  | 
422  |  |  | 
423  | 52  |   return arena->buffers[buffer_id].data + offset;  | 
424  | 4.03k  | }  | 
425  |  |  | 
426  |  | yr_arena_off_t yr_arena_get_current_offset(YR_ARENA* arena, uint32_t buffer_id)  | 
427  | 2  | { | 
428  | 2  |   assert(buffer_id < arena->num_buffers);  | 
429  |  |  | 
430  | 2  |   return (yr_arena_off_t) arena->buffers[buffer_id].used;  | 
431  | 2  | }  | 
432  |  |  | 
433  |  | int yr_arena_ptr_to_ref(YR_ARENA* arena, const void* address, YR_ARENA_REF* ref)  | 
434  | 23.8k  | { | 
435  | 23.8k  |   *ref = YR_ARENA_NULL_REF;  | 
436  |  |  | 
437  | 23.8k  |   if (address == NULL)  | 
438  | 0  |     return 1;  | 
439  |  |  | 
440  | 127k  |   for (uint32_t i = 0; i < arena->num_buffers; ++i)  | 
441  | 127k  |   { | 
442  |  |     // If the buffer is completetly empty, skip it.  | 
443  | 127k  |     if (arena->buffers[i].data == NULL)  | 
444  | 39.7k  |       continue;  | 
445  |  |  | 
446  |  |     // If the address falls within the limits of the buffer, then we found  | 
447  |  |     // the buffer that contains the data and return a reference to it.  | 
448  | 87.4k  |     if ((uint8_t*) address >= arena->buffers[i].data &&  | 
449  | 63.6k  |         (uint8_t*) address < arena->buffers[i].data + arena->buffers[i].used)  | 
450  | 23.8k  |     { | 
451  | 23.8k  |       ref->buffer_id = i;  | 
452  | 23.8k  |       ref->offset =  | 
453  | 23.8k  |           (yr_arena_off_t) ((uint8_t*) address - arena->buffers[i].data);  | 
454  |  |  | 
455  | 23.8k  |       return 1;  | 
456  | 23.8k  |     }  | 
457  | 87.4k  |   }  | 
458  |  |  | 
459  | 0  |   return 0;  | 
460  | 23.8k  | }  | 
461  |  |  | 
462  |  | void* yr_arena_ref_to_ptr(YR_ARENA* arena, YR_ARENA_REF* ref)  | 
463  | 36  | { | 
464  | 36  |   if (YR_ARENA_IS_NULL_REF(*ref))  | 
465  | 6  |     return NULL;  | 
466  |  |  | 
467  |  | #if defined(__arm__)  | 
468  |  |   YR_ARENA_REF tmp_ref;  | 
469  |  |   memcpy(&tmp_ref, ref, sizeof(YR_ARENA_REF));  | 
470  |  |   ref = &tmp_ref;  | 
471  |  | #endif  | 
472  |  |  | 
473  | 30  |   return yr_arena_get_ptr(arena, ref->buffer_id, ref->offset);  | 
474  | 36  | }  | 
475  |  |  | 
476  |  | ////////////////////////////////////////////////////////////////////////////////  | 
477  |  | // Tells the arena that certain addresses contains a relocatable pointer.  | 
478  |  | //  | 
479  |  | // Args:  | 
480  |  | //   arena: Pointer to the arena.  | 
481  |  | //   buffer_id: Buffer number.  | 
482  |  | //   ... : Variable number of size_t arguments with offsets within the buffer.  | 
483  |  | //  | 
484  |  | // Returns:  | 
485  |  | //    ERROR_SUCCESS if succeed or the corresponding error code otherwise.  | 
486  |  | //  | 
487  |  | int yr_arena_make_ptr_relocatable(YR_ARENA* arena, uint32_t buffer_id, ...)  | 
488  | 12  | { | 
489  | 12  |   int result;  | 
490  |  |  | 
491  | 12  |   va_list offsets;  | 
492  | 12  |   va_start(offsets, buffer_id);  | 
493  |  |  | 
494  | 12  |   result = _yr_arena_make_ptr_relocatable(arena, buffer_id, 0, offsets);  | 
495  |  |  | 
496  | 12  |   va_end(offsets);  | 
497  |  |  | 
498  | 12  |   return result;  | 
499  | 12  | }  | 
500  |  |  | 
501  |  | int yr_arena_write_data(  | 
502  |  |     YR_ARENA* arena,  | 
503  |  |     uint32_t buffer_id,  | 
504  |  |     const void* data,  | 
505  |  |     size_t size,  | 
506  |  |     YR_ARENA_REF* ref)  | 
507  | 66  | { | 
508  | 66  |   YR_ARENA_REF r;  | 
509  |  |  | 
510  |  |   // Allocate space in the buffer.  | 
511  | 66  |   FAIL_ON_ERROR(_yr_arena_allocate_memory(arena, 0, buffer_id, size, &r));  | 
512  |  |  | 
513  |  |   // Copy the data into the allocated space.  | 
514  | 66  |   memcpy(arena->buffers[buffer_id].data + r.offset, data, size);  | 
515  |  |  | 
516  | 66  |   if (ref != NULL)  | 
517  | 28  |   { | 
518  | 28  |     ref->buffer_id = r.buffer_id;  | 
519  | 28  |     ref->offset = r.offset;  | 
520  | 28  |   }  | 
521  |  |  | 
522  | 66  |   return ERROR_SUCCESS;  | 
523  | 66  | }  | 
524  |  |  | 
525  |  | int yr_arena_write_string(  | 
526  |  |     YR_ARENA* arena,  | 
527  |  |     uint32_t buffer_id,  | 
528  |  |     const char* string,  | 
529  |  |     YR_ARENA_REF* ref)  | 
530  | 0  | { | 
531  | 0  |   return yr_arena_write_data(arena, buffer_id, string, strlen(string) + 1, ref);  | 
532  | 0  | }  | 
533  |  |  | 
534  |  | int yr_arena_write_uint32(  | 
535  |  |     YR_ARENA* arena,  | 
536  |  |     uint32_t buffer_id,  | 
537  |  |     uint32_t integer,  | 
538  |  |     YR_ARENA_REF* ref)  | 
539  | 0  | { | 
540  | 0  |   return yr_arena_write_data(arena, buffer_id, &integer, sizeof(integer), ref);  | 
541  | 0  | }  | 
542  |  |  | 
543  |  | int yr_arena_load_stream(YR_STREAM* stream, YR_ARENA** arena)  | 
544  | 0  | { | 
545  | 0  |   YR_ARENA_FILE_HEADER hdr;  | 
546  |  | 
  | 
547  | 0  |   if (yr_stream_read(&hdr, sizeof(hdr), 1, stream) != 1)  | 
548  | 0  |     return ERROR_INVALID_FILE;  | 
549  |  |  | 
550  | 0  |   if (hdr.magic[0] != 'Y' || hdr.magic[1] != 'A' || hdr.magic[2] != 'R' ||  | 
551  | 0  |       hdr.magic[3] != 'A')  | 
552  | 0  |   { | 
553  | 0  |     return ERROR_INVALID_FILE;  | 
554  | 0  |   }  | 
555  |  |  | 
556  | 0  |   if (hdr.version != YR_ARENA_FILE_VERSION)  | 
557  | 0  |     return ERROR_UNSUPPORTED_FILE_VERSION;  | 
558  |  |  | 
559  | 0  |   if (hdr.num_buffers > YR_MAX_ARENA_BUFFERS)  | 
560  | 0  |     return ERROR_INVALID_FILE;  | 
561  |  |  | 
562  | 0  |   YR_ARENA_FILE_BUFFER buffers[YR_MAX_ARENA_BUFFERS];  | 
563  |  | 
  | 
564  | 0  |   size_t read = yr_stream_read(  | 
565  | 0  |       buffers, sizeof(buffers[0]), hdr.num_buffers, stream);  | 
566  |  | 
  | 
567  | 0  |   if (read != hdr.num_buffers)  | 
568  | 0  |     return ERROR_CORRUPT_FILE;  | 
569  |  |  | 
570  | 0  |   YR_ARENA* new_arena;  | 
571  |  | 
  | 
572  | 0  |   FAIL_ON_ERROR(yr_arena_create(hdr.num_buffers, 10485, &new_arena))  | 
573  |  |  | 
574  | 0  |   for (int i = 0; i < hdr.num_buffers; ++i)  | 
575  | 0  |   { | 
576  | 0  |     if (buffers[i].size == 0)  | 
577  | 0  |       continue;  | 
578  |  |  | 
579  | 0  |     YR_ARENA_REF ref;  | 
580  |  | 
  | 
581  | 0  |     FAIL_ON_ERROR_WITH_CLEANUP(  | 
582  | 0  |         yr_arena_allocate_memory(new_arena, i, buffers[i].size, &ref),  | 
583  | 0  |         yr_arena_release(new_arena))  | 
584  |  |  | 
585  | 0  |     void* ptr = yr_arena_get_ptr(new_arena, i, ref.offset);  | 
586  |  | 
  | 
587  | 0  |     if (yr_stream_read(ptr, buffers[i].size, 1, stream) != 1)  | 
588  | 0  |     { | 
589  | 0  |       yr_arena_release(new_arena);  | 
590  | 0  |       return ERROR_CORRUPT_FILE;  | 
591  | 0  |     }  | 
592  | 0  |   }  | 
593  |  |  | 
594  | 0  |   YR_ARENA_REF reloc_ref;  | 
595  |  | 
  | 
596  | 0  |   while (yr_stream_read(&reloc_ref, sizeof(reloc_ref), 1, stream) == 1)  | 
597  | 0  |   { | 
598  | 0  |     YR_ARENA_BUFFER* b = &new_arena->buffers[reloc_ref.buffer_id];  | 
599  |  | 
  | 
600  | 0  |     if (b->data == NULL ||   | 
601  | 0  |         b->used < sizeof(void*) ||  | 
602  | 0  |         b->used > b->size ||  | 
603  | 0  |         reloc_ref.buffer_id >= new_arena->num_buffers ||  | 
604  | 0  |         reloc_ref.offset > b->used - sizeof(void*))  | 
605  | 0  |     { | 
606  | 0  |       yr_arena_release(new_arena);  | 
607  | 0  |       return ERROR_CORRUPT_FILE;  | 
608  | 0  |     }  | 
609  |  |  | 
610  | 0  |     YR_ARENA_REF ref;  | 
611  |  | 
  | 
612  | 0  |     memcpy(&ref, b->data + reloc_ref.offset, sizeof(ref));  | 
613  |  | 
  | 
614  | 0  |     void* reloc_ptr = yr_arena_ref_to_ptr(new_arena, &ref);  | 
615  |  | 
  | 
616  | 0  |     memcpy(b->data + reloc_ref.offset, &reloc_ptr, sizeof(reloc_ptr));  | 
617  |  | 
  | 
618  | 0  |     FAIL_ON_ERROR_WITH_CLEANUP(  | 
619  | 0  |         yr_arena_make_ptr_relocatable(  | 
620  | 0  |             new_arena, reloc_ref.buffer_id, reloc_ref.offset, EOL),  | 
621  | 0  |         yr_arena_release(new_arena))  | 
622  | 0  |   }  | 
623  |  |  | 
624  | 0  |   *arena = new_arena;  | 
625  |  | 
  | 
626  | 0  |   return ERROR_SUCCESS;  | 
627  | 0  | }  | 
628  |  |  | 
629  |  | int yr_arena_save_stream(YR_ARENA* arena, YR_STREAM* stream)  | 
630  | 0  | { | 
631  | 0  |   YR_ARENA_FILE_HEADER hdr;  | 
632  |  | 
  | 
633  | 0  |   hdr.magic[0] = 'Y';  | 
634  | 0  |   hdr.magic[1] = 'A';  | 
635  | 0  |   hdr.magic[2] = 'R';  | 
636  | 0  |   hdr.magic[3] = 'A';  | 
637  |  | 
  | 
638  | 0  |   hdr.version = YR_ARENA_FILE_VERSION;  | 
639  | 0  |   hdr.num_buffers = arena->num_buffers;  | 
640  |  | 
  | 
641  | 0  |   if (yr_stream_write(&hdr, sizeof(hdr), 1, stream) != 1)  | 
642  | 0  |     return ERROR_WRITING_FILE;  | 
643  |  |  | 
644  |  |   // The first buffer in the file is after the header and the buffer table,  | 
645  |  |   // calculate its offset accordingly.  | 
646  | 0  |   uint64_t offset = sizeof(YR_ARENA_FILE_HEADER) +  | 
647  | 0  |                     sizeof(YR_ARENA_FILE_BUFFER) * arena->num_buffers;  | 
648  |  | 
  | 
649  | 0  |   for (uint32_t i = 0; i < arena->num_buffers; ++i)  | 
650  | 0  |   { | 
651  | 0  |     YR_ARENA_FILE_BUFFER buffer = { | 
652  | 0  |         .offset = offset,  | 
653  | 0  |         .size = (uint32_t) arena->buffers[i].used,  | 
654  | 0  |     };  | 
655  |  | 
  | 
656  | 0  |     if (yr_stream_write(&buffer, sizeof(buffer), 1, stream) != 1)  | 
657  | 0  |       return ERROR_WRITING_FILE;  | 
658  |  |  | 
659  | 0  |     offset += buffer.size;  | 
660  | 0  |   }  | 
661  |  |  | 
662  |  |   // Iterate the relocation list and replace all the relocatable pointers by  | 
663  |  |   // references to the buffer and offset where they are pointing to. All  | 
664  |  |   // relocatable pointers are expected to be null or point to data stored in  | 
665  |  |   // some of the arena's buffers. If a relocatable pointer points outside the  | 
666  |  |   // arena that's an error.  | 
667  | 0  |   YR_RELOC* reloc = arena->reloc_list_head;  | 
668  |  | 
  | 
669  | 0  |   while (reloc != NULL)  | 
670  | 0  |   { | 
671  |  |     // reloc_ptr is the pointer that will be replaced by the reference.  | 
672  | 0  |     void* reloc_ptr;  | 
673  |  |  | 
674  |  |     // Move the pointer from the buffer to reloc_ptr.  | 
675  | 0  |     memcpy(  | 
676  | 0  |         &reloc_ptr,  | 
677  | 0  |         arena->buffers[reloc->buffer_id].data + reloc->offset,  | 
678  | 0  |         sizeof(reloc_ptr));  | 
679  |  | 
  | 
680  | 0  |     YR_ARENA_REF ref;  | 
681  |  | 
  | 
682  | 0  | #if !defined(NDEBUG)  | 
683  | 0  |     int found = yr_arena_ptr_to_ref(arena, reloc_ptr, &ref);  | 
684  |  |     // yr_arena_ptr_to_ref returns 0 if the relocatable pointer is pointing  | 
685  |  |     // outside the arena, this should not happen.  | 
686  | 0  |     assert(found);  | 
687  |  | #else  | 
688  |  |     yr_arena_ptr_to_ref(arena, reloc_ptr, &ref);  | 
689  |  | #endif  | 
690  |  |  | 
691  |  |     // Replace the relocatable pointer with a reference that holds information  | 
692  |  |     // about the buffer and offset where the relocatable pointer is pointing to.  | 
693  | 0  |     memcpy(  | 
694  | 0  |         arena->buffers[reloc->buffer_id].data + reloc->offset,  | 
695  | 0  |         &ref,  | 
696  | 0  |         sizeof(ref));  | 
697  |  | 
  | 
698  | 0  |     reloc = reloc->next;  | 
699  | 0  |   }  | 
700  |  |  | 
701  |  |   // Now that all relocatable pointers are converted to references, write the  | 
702  |  |   // buffers.  | 
703  | 0  |   for (uint32_t i = 0; i < arena->num_buffers; ++i)  | 
704  | 0  |   { | 
705  | 0  |     YR_ARENA_BUFFER* b = &arena->buffers[i];  | 
706  |  | 
  | 
707  | 0  |     if (b->used > 0)  | 
708  | 0  |       if (yr_stream_write(b->data, b->used, 1, stream) != 1)  | 
709  | 0  |         return ERROR_WRITING_FILE;  | 
710  | 0  |   }  | 
711  |  |  | 
712  |  |   // Write the relocation list and restore the pointers back.  | 
713  | 0  |   reloc = arena->reloc_list_head;  | 
714  |  | 
  | 
715  | 0  |   while (reloc != NULL)  | 
716  | 0  |   { | 
717  | 0  |     YR_ARENA_REF ref = { | 
718  | 0  |         .buffer_id = reloc->buffer_id,  | 
719  | 0  |         .offset = reloc->offset,  | 
720  | 0  |     };  | 
721  |  |  | 
722  |  |     // Write the relocation entry, which consists in a reference to the place  | 
723  |  |     // where the pointer that needs to be relocated is stored.  | 
724  | 0  |     if (yr_stream_write(&ref, sizeof(ref), 1, stream) != 1)  | 
725  | 0  |       return ERROR_WRITING_FILE;  | 
726  |  |  | 
727  |  |     // Move the reference that is going to be replaced by the corresponding  | 
728  |  |     // pointer to the ref variable. Notice that ref is being reused for a  | 
729  |  |     // different reference here.  | 
730  | 0  |     memcpy(  | 
731  | 0  |         &ref,  | 
732  | 0  |         arena->buffers[reloc->buffer_id].data + reloc->offset,  | 
733  | 0  |         sizeof(ref));  | 
734  |  |  | 
735  |  |     // Convert the reference to its corresponding pointer.  | 
736  | 0  |     void* reloc_ptr = yr_arena_ref_to_ptr(arena, &ref);  | 
737  |  |  | 
738  |  |     // Copy the pointer back to where the reference was stored.  | 
739  | 0  |     memcpy(  | 
740  | 0  |         arena->buffers[reloc->buffer_id].data + reloc->offset,  | 
741  | 0  |         &reloc_ptr,  | 
742  | 0  |         sizeof(reloc_ptr));  | 
743  |  | 
  | 
744  | 0  |     reloc = reloc->next;  | 
745  | 0  |   }  | 
746  |  |  | 
747  | 0  |   return ERROR_SUCCESS;  | 
748  | 0  | }  |