Coverage Report

Created: 2025-07-18 06:53

/src/yara/libyara/arena.c
Line
Count
Source (jump to first uncovered line)
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
4.35k
{
238
4.35k
  YR_ARENA* new_arena = (YR_ARENA*) yr_calloc(1, sizeof(YR_ARENA));
239
240
4.35k
  if (new_arena == NULL)
241
0
    return ERROR_INSUFFICIENT_MEMORY;
242
243
4.35k
  new_arena->xrefs = 1;
244
4.35k
  new_arena->num_buffers = num_buffers;
245
4.35k
  new_arena->initial_buffer_size = initial_buffer_size;
246
247
4.35k
  *arena = new_arena;
248
249
4.35k
  return ERROR_SUCCESS;
250
4.35k
}
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
4.35k
{
273
4.35k
  arena->xrefs--;
274
275
4.35k
  if (arena->xrefs > 0)
276
2
    return ERROR_SUCCESS;
277
278
8.70k
  for (uint32_t i = 0; i < arena->num_buffers; i++)
279
4.35k
  {
280
4.35k
    if (arena->buffers[i].data != NULL)
281
0
      yr_free(arena->buffers[i].data);
282
4.35k
  }
283
284
4.35k
  YR_RELOC* reloc = arena->reloc_list_head;
285
286
4.35k
  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
4.35k
  yr_free(arena);
294
295
4.35k
  return ERROR_SUCCESS;
296
4.35k
}
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.40k
{
417
4.40k
  assert(buffer_id < arena->num_buffers);
418
4.40k
  assert(offset <= arena->buffers[buffer_id].used);
419
420
4.40k
  if (arena->buffers[buffer_id].data == NULL)
421
4.35k
    return NULL;
422
423
52
  return arena->buffers[buffer_id].data + offset;
424
4.40k
}
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
26.1k
{
435
26.1k
  *ref = YR_ARENA_NULL_REF;
436
437
26.1k
  if (address == NULL)
438
0
    return 1;
439
440
139k
  for (uint32_t i = 0; i < arena->num_buffers; ++i)
441
139k
  {
442
    // If the buffer is completetly empty, skip it.
443
139k
    if (arena->buffers[i].data == NULL)
444
43.5k
      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
95.7k
    if ((uint8_t*) address >= arena->buffers[i].data &&
449
95.7k
        (uint8_t*) address < arena->buffers[i].data + arena->buffers[i].used)
450
26.1k
    {
451
26.1k
      ref->buffer_id = i;
452
26.1k
      ref->offset =
453
26.1k
          (yr_arena_off_t) ((uint8_t*) address - arena->buffers[i].data);
454
455
26.1k
      return 1;
456
26.1k
    }
457
95.7k
  }
458
459
0
  return 0;
460
26.1k
}
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 (reloc_ref.buffer_id >= new_arena->num_buffers ||
601
0
        reloc_ref.offset > b->used - sizeof(void*) ||
602
0
        b->data == NULL)
603
0
    {
604
0
      yr_arena_release(new_arena);
605
0
      return ERROR_CORRUPT_FILE;
606
0
    }
607
608
0
    YR_ARENA_REF ref;
609
610
0
    memcpy(&ref, b->data + reloc_ref.offset, sizeof(ref));
611
612
0
    void* reloc_ptr = yr_arena_ref_to_ptr(new_arena, &ref);
613
614
0
    memcpy(b->data + reloc_ref.offset, &reloc_ptr, sizeof(reloc_ptr));
615
616
0
    FAIL_ON_ERROR_WITH_CLEANUP(
617
0
        yr_arena_make_ptr_relocatable(
618
0
            new_arena, reloc_ref.buffer_id, reloc_ref.offset, EOL),
619
0
        yr_arena_release(new_arena))
620
0
  }
621
622
0
  *arena = new_arena;
623
624
0
  return ERROR_SUCCESS;
625
0
}
626
627
int yr_arena_save_stream(YR_ARENA* arena, YR_STREAM* stream)
628
0
{
629
0
  YR_ARENA_FILE_HEADER hdr;
630
631
0
  hdr.magic[0] = 'Y';
632
0
  hdr.magic[1] = 'A';
633
0
  hdr.magic[2] = 'R';
634
0
  hdr.magic[3] = 'A';
635
636
0
  hdr.version = YR_ARENA_FILE_VERSION;
637
0
  hdr.num_buffers = arena->num_buffers;
638
639
0
  if (yr_stream_write(&hdr, sizeof(hdr), 1, stream) != 1)
640
0
    return ERROR_WRITING_FILE;
641
642
  // The first buffer in the file is after the header and the buffer table,
643
  // calculate its offset accordingly.
644
0
  uint64_t offset = sizeof(YR_ARENA_FILE_HEADER) +
645
0
                    sizeof(YR_ARENA_FILE_BUFFER) * arena->num_buffers;
646
647
0
  for (uint32_t i = 0; i < arena->num_buffers; ++i)
648
0
  {
649
0
    YR_ARENA_FILE_BUFFER buffer = {
650
0
        .offset = offset,
651
0
        .size = (uint32_t) arena->buffers[i].used,
652
0
    };
653
654
0
    if (yr_stream_write(&buffer, sizeof(buffer), 1, stream) != 1)
655
0
      return ERROR_WRITING_FILE;
656
657
0
    offset += buffer.size;
658
0
  }
659
660
  // Iterate the relocation list and replace all the relocatable pointers by
661
  // references to the buffer and offset where they are pointing to. All
662
  // relocatable pointers are expected to be null or point to data stored in
663
  // some of the arena's buffers. If a relocatable pointer points outside the
664
  // arena that's an error.
665
0
  YR_RELOC* reloc = arena->reloc_list_head;
666
667
0
  while (reloc != NULL)
668
0
  {
669
    // reloc_ptr is the pointer that will be replaced by the reference.
670
0
    void* reloc_ptr;
671
672
    // Move the pointer from the buffer to reloc_ptr.
673
0
    memcpy(
674
0
        &reloc_ptr,
675
0
        arena->buffers[reloc->buffer_id].data + reloc->offset,
676
0
        sizeof(reloc_ptr));
677
678
0
    YR_ARENA_REF ref;
679
680
0
#if !defined(NDEBUG)
681
0
    int found = yr_arena_ptr_to_ref(arena, reloc_ptr, &ref);
682
    // yr_arena_ptr_to_ref returns 0 if the relocatable pointer is pointing
683
    // outside the arena, this should not happen.
684
0
    assert(found);
685
#else
686
    yr_arena_ptr_to_ref(arena, reloc_ptr, &ref);
687
#endif
688
689
    // Replace the relocatable pointer with a reference that holds information
690
    // about the buffer and offset where the relocatable pointer is pointing to.
691
0
    memcpy(
692
0
        arena->buffers[reloc->buffer_id].data + reloc->offset,
693
0
        &ref,
694
0
        sizeof(ref));
695
696
0
    reloc = reloc->next;
697
0
  }
698
699
  // Now that all relocatable pointers are converted to references, write the
700
  // buffers.
701
0
  for (uint32_t i = 0; i < arena->num_buffers; ++i)
702
0
  {
703
0
    YR_ARENA_BUFFER* b = &arena->buffers[i];
704
705
0
    if (b->used > 0)
706
0
      if (yr_stream_write(b->data, b->used, 1, stream) != 1)
707
0
        return ERROR_WRITING_FILE;
708
0
  }
709
710
  // Write the relocation list and restore the pointers back.
711
0
  reloc = arena->reloc_list_head;
712
713
0
  while (reloc != NULL)
714
0
  {
715
0
    YR_ARENA_REF ref = {
716
0
        .buffer_id = reloc->buffer_id,
717
0
        .offset = reloc->offset,
718
0
    };
719
720
    // Write the relocation entry, which consists in a reference to the place
721
    // where the pointer that needs to be relocated is stored.
722
0
    if (yr_stream_write(&ref, sizeof(ref), 1, stream) != 1)
723
0
      return ERROR_WRITING_FILE;
724
725
    // Move the reference that is going to be replaced by the corresponding
726
    // pointer to the ref variable. Notice that ref is being reused for a
727
    // different reference here.
728
0
    memcpy(
729
0
        &ref,
730
0
        arena->buffers[reloc->buffer_id].data + reloc->offset,
731
0
        sizeof(ref));
732
733
    // Convert the reference to its corresponding pointer.
734
0
    void* reloc_ptr = yr_arena_ref_to_ptr(arena, &ref);
735
736
    // Copy the pointer back to where the reference was stored.
737
0
    memcpy(
738
0
        arena->buffers[reloc->buffer_id].data + reloc->offset,
739
0
        &reloc_ptr,
740
0
        sizeof(reloc_ptr));
741
742
0
    reloc = reloc->next;
743
0
  }
744
745
0
  return ERROR_SUCCESS;
746
0
}