Coverage Report

Created: 2026-07-10 06:40

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