Coverage Report

Created: 2026-08-28 06:16

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
9
{
82
9
  size_t offset;
83
84
9
  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
9
  offset = va_arg(offsets, size_t);
89
90
21
  while (offset != EOL)
91
12
  {
92
12
    YR_RELOC* reloc = (YR_RELOC*) yr_malloc(sizeof(YR_RELOC));
93
94
12
    if (reloc == NULL)
95
0
      return ERROR_INSUFFICIENT_MEMORY;
96
97
12
    reloc->buffer_id = buffer_id;
98
12
    reloc->offset = base_offset + (yr_arena_off_t) offset;
99
12
    reloc->next = NULL;
100
101
12
    if (arena->reloc_list_head == NULL)
102
1
      arena->reloc_list_head = reloc;
103
104
12
    if (arena->reloc_list_tail != NULL)
105
11
      arena->reloc_list_tail->next = reloc;
106
107
12
    arena->reloc_list_tail = reloc;
108
12
    offset = va_arg(offsets, size_t);
109
12
  }
110
111
9
  return result;
112
9
}
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
38
{
140
38
  if (buffer_id >= arena->num_buffers)
141
0
    return ERROR_INVALID_ARGUMENT;
142
143
38
  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
38
  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
38
  if (ref != NULL)
210
36
  {
211
36
    ref->buffer_id = buffer_id;
212
36
    ref->offset = (uint32_t) b->used;
213
36
  }
214
215
38
  b->used += size;
216
217
38
  return ERROR_SUCCESS;
218
38
}
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
  if (num_buffers == 0 || num_buffers > YR_MAX_ARENA_BUFFERS)
238
0
    return ERROR_INVALID_ARGUMENT;
239
240
1
  YR_ARENA* new_arena = (YR_ARENA*) yr_calloc(1, sizeof(YR_ARENA));
241
242
1
  if (new_arena == NULL)
243
0
    return ERROR_INSUFFICIENT_MEMORY;
244
245
1
  new_arena->xrefs = 1;
246
1
  new_arena->num_buffers = num_buffers;
247
1
  new_arena->initial_buffer_size = initial_buffer_size;
248
249
1
  *arena = new_arena;
250
251
1
  return ERROR_SUCCESS;
252
1
}
253
254
void yr_arena_acquire(YR_ARENA* arena)
255
1
{
256
1
  arena->xrefs++;
257
1
}
258
259
////////////////////////////////////////////////////////////////////////////////
260
// Releases the arena.
261
//
262
// Decrements the cross-references counter for the arena, if the number of
263
// cross-references reaches zero the arena is destroyed and all its resources
264
// are freed.
265
//
266
// Args:
267
//   arena :Pointer to the arena.
268
//
269
// Returns:
270
//   ERROR_SUCCESS
271
//   ERROR_INSUFFICIENT_MEMORY
272
//
273
int yr_arena_release(YR_ARENA* arena)
274
1
{
275
1
  arena->xrefs--;
276
277
1
  if (arena->xrefs > 0)
278
1
    return ERROR_SUCCESS;
279
280
0
  for (uint32_t i = 0; i < arena->num_buffers; i++)
281
0
  {
282
0
    if (arena->buffers[i].data != NULL)
283
0
      yr_free(arena->buffers[i].data);
284
0
  }
285
286
0
  YR_RELOC* reloc = arena->reloc_list_head;
287
288
0
  while (reloc != NULL)
289
0
  {
290
0
    YR_RELOC* next = reloc->next;
291
0
    yr_free(reloc);
292
0
    reloc = next;
293
0
  }
294
295
0
  yr_free(arena);
296
297
0
  return ERROR_SUCCESS;
298
1
}
299
300
////////////////////////////////////////////////////////////////////////////////
301
// Allocates memory in a buffer within the arena.
302
//
303
// Args:
304
//   arena: Pointer to the arena.
305
//   buffer_id: Buffer number.
306
//   size : Size of the region to be allocated.
307
//   [out] offset: Pointer to a variable where the function puts the offset
308
//                 within the buffer of the allocated region. The pointer can
309
//                 be NULL.
310
// Returns:
311
//   ERROR_SUCCESS
312
//   ERROR_INVALID_ARGUMENT
313
//   ERROR_INSUFFICIENT_MEMORY
314
//
315
int yr_arena_allocate_memory(
316
    YR_ARENA* arena,
317
    uint32_t buffer_id,
318
    size_t size,
319
    YR_ARENA_REF* ref)
320
0
{
321
0
  return _yr_arena_allocate_memory(arena, 0, buffer_id, size, ref);
322
0
}
323
324
////////////////////////////////////////////////////////////////////////////////
325
// Allocates memory in a buffer within the arena and fill it with zeroes.
326
//
327
// Args:
328
//   arena: Pointer to the arena.
329
//   buffer_id: Buffer number.
330
//   size : Size of the region to be allocated.
331
//   [out] offset: Pointer to a variable where the function puts the offset
332
//                 within the buffer of the allocated region. The pointer can
333
//                 be NULL.
334
// Returns:
335
//   ERROR_SUCCESS
336
//   ERROR_INVALID_ARGUMENT
337
//   ERROR_INSUFFICIENT_MEMORY
338
//
339
int yr_arena_allocate_zeroed_memory(
340
    YR_ARENA* arena,
341
    uint32_t buffer_id,
342
    size_t size,
343
    YR_ARENA_REF* ref)
344
2
{
345
2
  return _yr_arena_allocate_memory(
346
2
      arena, YR_ARENA_ZERO_MEMORY, buffer_id, size, ref);
347
2
}
348
349
////////////////////////////////////////////////////////////////////////////////
350
// Allocates a structure within the arena. This function is similar to
351
// yr_arena_allocate_memory but additionally receives a variable-length
352
// list of offsets within the structure where pointers reside. This allows
353
// the arena to keep track of pointers that must be adjusted when memory
354
// is relocated. This is an example on how to invoke this function:
355
//
356
//  yr_arena_allocate_struct(
357
//        arena,
358
//        0,
359
//        sizeof(MY_STRUCTURE),
360
//        &ref,
361
//        offsetof(MY_STRUCTURE, field_1),
362
//        offsetof(MY_STRUCTURE, field_2),
363
//        ..
364
//        offsetof(MY_STRUCTURE, field_N),
365
//        EOL);
366
//
367
// Args:
368
//   arena: Pointer to the arena.
369
//   buffer_id: Buffer number.
370
//   size: Size of the region to be allocated.
371
//   [out] ref: Pointer to a reference that will point to the newly allocated
372
//              structure when the function returns. The pointer can be NULL
373
//              if you don't need the reference.
374
//   ...   Variable number of offsets relative to the beginning of the struct.
375
//         These offsets are of type size_t.
376
//
377
// Returns:
378
//   ERROR_SUCCESS
379
//   ERROR_INVALID_ARGUMENT
380
//   ERROR_INSUFFICIENT_MEMORY
381
//
382
int yr_arena_allocate_struct(
383
    YR_ARENA* arena,
384
    uint32_t buffer_id,
385
    size_t size,
386
    YR_ARENA_REF* ref,
387
    ...)
388
3
{
389
3
  YR_ARENA_REF r;
390
391
3
  int result = _yr_arena_allocate_memory(
392
3
      arena, YR_ARENA_ZERO_MEMORY, buffer_id, size, &r);
393
394
3
  if (result != ERROR_SUCCESS)
395
0
    return result;
396
397
3
  va_list field_offsets;
398
3
  va_start(field_offsets, ref);
399
400
3
  result = _yr_arena_make_ptr_relocatable(
401
3
      arena, buffer_id, r.offset, field_offsets);
402
403
3
  va_end(field_offsets);
404
405
3
  if (result == ERROR_SUCCESS && ref != NULL)
406
3
  {
407
3
    ref->buffer_id = r.buffer_id;
408
3
    ref->offset = r.offset;
409
3
  }
410
411
3
  return result;
412
3
}
413
414
void* yr_arena_get_ptr(
415
    YR_ARENA* arena,
416
    uint32_t buffer_id,
417
    yr_arena_off_t offset)
418
31
{
419
31
  assert(buffer_id < arena->num_buffers);
420
31
  assert(offset <= arena->buffers[buffer_id].used);
421
422
31
  if (arena->buffers[buffer_id].data == NULL)
423
3
    return NULL;
424
425
28
  return arena->buffers[buffer_id].data + offset;
426
31
}
427
428
yr_arena_off_t yr_arena_get_current_offset(YR_ARENA* arena, uint32_t buffer_id)
429
7
{
430
7
  assert(buffer_id < arena->num_buffers);
431
432
7
  return (yr_arena_off_t) arena->buffers[buffer_id].used;
433
7
}
434
435
int yr_arena_ptr_to_ref(YR_ARENA* arena, const void* address, YR_ARENA_REF* ref)
436
0
{
437
0
  *ref = YR_ARENA_NULL_REF;
438
439
0
  if (address == NULL)
440
0
    return 1;
441
442
0
  for (uint32_t i = 0; i < arena->num_buffers; ++i)
443
0
  {
444
    // If the buffer is completetly empty, skip it.
445
0
    if (arena->buffers[i].data == NULL)
446
0
      continue;
447
448
    // If the address falls within the limits of the buffer, then we found
449
    // the buffer that contains the data and return a reference to it.
450
0
    if ((uint8_t*) address >= arena->buffers[i].data &&
451
0
        (uint8_t*) address < arena->buffers[i].data + arena->buffers[i].used)
452
0
    {
453
0
      ref->buffer_id = i;
454
0
      ref->offset =
455
0
          (yr_arena_off_t) ((uint8_t*) address - arena->buffers[i].data);
456
457
0
      return 1;
458
0
    }
459
0
  }
460
461
0
  return 0;
462
0
}
463
464
void* yr_arena_ref_to_ptr(YR_ARENA* arena, YR_ARENA_REF* ref)
465
18
{
466
18
  if (YR_ARENA_IS_NULL_REF(*ref))
467
3
    return NULL;
468
469
#if defined(__arm__)
470
  YR_ARENA_REF tmp_ref;
471
  memcpy(&tmp_ref, ref, sizeof(YR_ARENA_REF));
472
  ref = &tmp_ref;
473
#endif
474
475
15
  return yr_arena_get_ptr(arena, ref->buffer_id, ref->offset);
476
18
}
477
478
////////////////////////////////////////////////////////////////////////////////
479
// Tells the arena that certain addresses contains a relocatable pointer.
480
//
481
// Args:
482
//   arena: Pointer to the arena.
483
//   buffer_id: Buffer number.
484
//   ... : Variable number of size_t arguments with offsets within the buffer.
485
//
486
// Returns:
487
//    ERROR_SUCCESS if succeed or the corresponding error code otherwise.
488
//
489
int yr_arena_make_ptr_relocatable(YR_ARENA* arena, uint32_t buffer_id, ...)
490
6
{
491
6
  int result;
492
493
6
  va_list offsets;
494
6
  va_start(offsets, buffer_id);
495
496
6
  result = _yr_arena_make_ptr_relocatable(arena, buffer_id, 0, offsets);
497
498
6
  va_end(offsets);
499
500
6
  return result;
501
6
}
502
503
int yr_arena_write_data(
504
    YR_ARENA* arena,
505
    uint32_t buffer_id,
506
    const void* data,
507
    size_t size,
508
    YR_ARENA_REF* ref)
509
33
{
510
33
  YR_ARENA_REF r;
511
512
  // Allocate space in the buffer.
513
33
  FAIL_ON_ERROR(_yr_arena_allocate_memory(arena, 0, buffer_id, size, &r));
514
515
  // Copy the data into the allocated space.
516
33
  memcpy(arena->buffers[buffer_id].data + r.offset, data, size);
517
518
33
  if (ref != NULL)
519
14
  {
520
14
    ref->buffer_id = r.buffer_id;
521
14
    ref->offset = r.offset;
522
14
  }
523
524
33
  return ERROR_SUCCESS;
525
33
}
526
527
int yr_arena_write_string(
528
    YR_ARENA* arena,
529
    uint32_t buffer_id,
530
    const char* string,
531
    YR_ARENA_REF* ref)
532
0
{
533
0
  return yr_arena_write_data(arena, buffer_id, string, strlen(string) + 1, ref);
534
0
}
535
536
int yr_arena_write_uint32(
537
    YR_ARENA* arena,
538
    uint32_t buffer_id,
539
    uint32_t integer,
540
    YR_ARENA_REF* ref)
541
0
{
542
0
  return yr_arena_write_data(arena, buffer_id, &integer, sizeof(integer), ref);
543
0
}
544
545
int yr_arena_load_stream(YR_STREAM* stream, YR_ARENA** arena)
546
0
{
547
0
  YR_ARENA_FILE_HEADER hdr;
548
549
0
  if (yr_stream_read(&hdr, sizeof(hdr), 1, stream) != 1)
550
0
    return ERROR_INVALID_FILE;
551
552
0
  if (hdr.magic[0] != 'Y' || hdr.magic[1] != 'A' || hdr.magic[2] != 'R' ||
553
0
      hdr.magic[3] != 'A')
554
0
  {
555
0
    return ERROR_INVALID_FILE;
556
0
  }
557
558
0
  if (hdr.version != YR_ARENA_FILE_VERSION)
559
0
    return ERROR_UNSUPPORTED_FILE_VERSION;
560
561
0
  if (hdr.num_buffers == 0 || hdr.num_buffers > YR_MAX_ARENA_BUFFERS)
562
0
    return ERROR_CORRUPT_FILE;
563
564
0
  YR_ARENA_FILE_BUFFER buffers[YR_MAX_ARENA_BUFFERS];
565
566
0
  size_t read = yr_stream_read(
567
0
      buffers, sizeof(buffers[0]), hdr.num_buffers, stream);
568
569
0
  if (read != hdr.num_buffers)
570
0
    return ERROR_CORRUPT_FILE;
571
572
0
  YR_ARENA* new_arena;
573
574
0
  FAIL_ON_ERROR(yr_arena_create(hdr.num_buffers, 10485, &new_arena))
575
576
0
  for (int i = 0; i < hdr.num_buffers; ++i)
577
0
  {
578
0
    if (buffers[i].size == 0)
579
0
      continue;
580
581
0
    YR_ARENA_REF ref;
582
583
0
    FAIL_ON_ERROR_WITH_CLEANUP(
584
0
        yr_arena_allocate_memory(new_arena, i, buffers[i].size, &ref),
585
0
        yr_arena_release(new_arena))
586
587
0
    void* ptr = yr_arena_get_ptr(new_arena, i, ref.offset);
588
589
0
    if (yr_stream_read(ptr, buffers[i].size, 1, stream) != 1)
590
0
    {
591
0
      yr_arena_release(new_arena);
592
0
      return ERROR_CORRUPT_FILE;
593
0
    }
594
0
  }
595
596
0
  YR_ARENA_REF reloc_ref;
597
598
0
  while (yr_stream_read(&reloc_ref, sizeof(reloc_ref), 1, stream) == 1)
599
0
  {
600
0
    if (reloc_ref.buffer_id >= new_arena->num_buffers)
601
0
    {
602
0
      yr_arena_release(new_arena);
603
0
      return ERROR_CORRUPT_FILE;
604
0
    }
605
606
0
    YR_ARENA_BUFFER* b = &new_arena->buffers[reloc_ref.buffer_id];
607
608
0
    if (b->data == NULL ||
609
0
        b->used < sizeof(YR_ARENA_REF) ||
610
0
        b->used > b->size ||
611
0
        reloc_ref.offset > b->used - sizeof(YR_ARENA_REF))
612
0
    {
613
0
      yr_arena_release(new_arena);
614
0
      return ERROR_CORRUPT_FILE;
615
0
    }
616
617
0
    YR_ARENA_REF ref;
618
619
0
    memcpy(&ref, b->data + reloc_ref.offset, sizeof(ref));
620
621
    // ref is the relocation target read from the loaded buffer, so its
622
    // buffer_id and offset come straight from the stream. yr_arena_get_ptr
623
    // only guards them with assert, which is a no-op under NDEBUG and would
624
    // index past the buffers array, so reject an out-of-range target here.
625
0
    if (!YR_ARENA_IS_NULL_REF(ref) &&
626
0
        (ref.buffer_id >= new_arena->num_buffers ||
627
0
         ref.offset > new_arena->buffers[ref.buffer_id].used))
628
0
    {
629
0
      yr_arena_release(new_arena);
630
0
      return ERROR_CORRUPT_FILE;
631
0
    }
632
633
0
    void* reloc_ptr = yr_arena_ref_to_ptr(new_arena, &ref);
634
635
0
    memcpy(b->data + reloc_ref.offset, &reloc_ptr, sizeof(reloc_ptr));
636
637
0
    FAIL_ON_ERROR_WITH_CLEANUP(
638
0
        yr_arena_make_ptr_relocatable(
639
0
            new_arena, reloc_ref.buffer_id, reloc_ref.offset, EOL),
640
0
        yr_arena_release(new_arena))
641
0
  }
642
643
0
  *arena = new_arena;
644
645
0
  return ERROR_SUCCESS;
646
0
}
647
648
int yr_arena_save_stream(YR_ARENA* arena, YR_STREAM* stream)
649
0
{
650
0
  YR_ARENA_FILE_HEADER hdr;
651
652
0
  hdr.magic[0] = 'Y';
653
0
  hdr.magic[1] = 'A';
654
0
  hdr.magic[2] = 'R';
655
0
  hdr.magic[3] = 'A';
656
657
0
  hdr.version = YR_ARENA_FILE_VERSION;
658
0
  hdr.num_buffers = arena->num_buffers;
659
660
0
  if (yr_stream_write(&hdr, sizeof(hdr), 1, stream) != 1)
661
0
    return ERROR_WRITING_FILE;
662
663
  // The first buffer in the file is after the header and the buffer table,
664
  // calculate its offset accordingly.
665
0
  uint64_t offset = sizeof(YR_ARENA_FILE_HEADER) +
666
0
                    sizeof(YR_ARENA_FILE_BUFFER) * arena->num_buffers;
667
668
0
  for (uint32_t i = 0; i < arena->num_buffers; ++i)
669
0
  {
670
0
    YR_ARENA_FILE_BUFFER buffer = {
671
0
        .offset = offset,
672
0
        .size = (uint32_t) arena->buffers[i].used,
673
0
    };
674
675
0
    if (yr_stream_write(&buffer, sizeof(buffer), 1, stream) != 1)
676
0
      return ERROR_WRITING_FILE;
677
678
0
    offset += buffer.size;
679
0
  }
680
681
  // Iterate the relocation list and replace all the relocatable pointers by
682
  // references to the buffer and offset where they are pointing to. All
683
  // relocatable pointers are expected to be null or point to data stored in
684
  // some of the arena's buffers. If a relocatable pointer points outside the
685
  // arena that's an error.
686
0
  YR_RELOC* reloc = arena->reloc_list_head;
687
688
0
  while (reloc != NULL)
689
0
  {
690
    // reloc_ptr is the pointer that will be replaced by the reference.
691
0
    void* reloc_ptr;
692
693
    // Move the pointer from the buffer to reloc_ptr.
694
0
    memcpy(
695
0
        &reloc_ptr,
696
0
        arena->buffers[reloc->buffer_id].data + reloc->offset,
697
0
        sizeof(reloc_ptr));
698
699
0
    YR_ARENA_REF ref;
700
701
0
#if !defined(NDEBUG)
702
0
    int found = yr_arena_ptr_to_ref(arena, reloc_ptr, &ref);
703
    // yr_arena_ptr_to_ref returns 0 if the relocatable pointer is pointing
704
    // outside the arena, this should not happen.
705
0
    assert(found);
706
#else
707
    yr_arena_ptr_to_ref(arena, reloc_ptr, &ref);
708
#endif
709
710
    // Replace the relocatable pointer with a reference that holds information
711
    // about the buffer and offset where the relocatable pointer is pointing to.
712
0
    memcpy(
713
0
        arena->buffers[reloc->buffer_id].data + reloc->offset,
714
0
        &ref,
715
0
        sizeof(ref));
716
717
0
    reloc = reloc->next;
718
0
  }
719
720
  // Now that all relocatable pointers are converted to references, write the
721
  // buffers.
722
0
  for (uint32_t i = 0; i < arena->num_buffers; ++i)
723
0
  {
724
0
    YR_ARENA_BUFFER* b = &arena->buffers[i];
725
726
0
    if (b->used > 0)
727
0
      if (yr_stream_write(b->data, b->used, 1, stream) != 1)
728
0
        return ERROR_WRITING_FILE;
729
0
  }
730
731
  // Write the relocation list and restore the pointers back.
732
0
  reloc = arena->reloc_list_head;
733
734
0
  while (reloc != NULL)
735
0
  {
736
0
    YR_ARENA_REF ref = {
737
0
        .buffer_id = reloc->buffer_id,
738
0
        .offset = reloc->offset,
739
0
    };
740
741
    // Write the relocation entry, which consists in a reference to the place
742
    // where the pointer that needs to be relocated is stored.
743
0
    if (yr_stream_write(&ref, sizeof(ref), 1, stream) != 1)
744
0
      return ERROR_WRITING_FILE;
745
746
    // Move the reference that is going to be replaced by the corresponding
747
    // pointer to the ref variable. Notice that ref is being reused for a
748
    // different reference here.
749
0
    memcpy(
750
0
        &ref,
751
0
        arena->buffers[reloc->buffer_id].data + reloc->offset,
752
0
        sizeof(ref));
753
754
    // Convert the reference to its corresponding pointer.
755
0
    void* reloc_ptr = yr_arena_ref_to_ptr(arena, &ref);
756
757
    // Copy the pointer back to where the reference was stored.
758
0
    memcpy(
759
0
        arena->buffers[reloc->buffer_id].data + reloc->offset,
760
0
        &reloc_ptr,
761
0
        sizeof(reloc_ptr));
762
763
0
    reloc = reloc->next;
764
0
  }
765
766
0
  return ERROR_SUCCESS;
767
0
}