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
3.60M
{
81
3.60M
  size_t offset;
82
83
3.60M
  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
3.60M
  offset = va_arg(offsets, size_t);
88
89
14.4M
  while (offset != EOL)
90
10.8M
  {
91
10.8M
    YR_RELOC* reloc = (YR_RELOC*) yr_malloc(sizeof(YR_RELOC));
92
93
10.8M
    if (reloc == NULL)
94
0
      return ERROR_INSUFFICIENT_MEMORY;
95
96
10.8M
    reloc->buffer_id = buffer_id;
97
10.8M
    reloc->offset = base_offset + (yr_arena_off_t) offset;
98
10.8M
    reloc->next = NULL;
99
100
10.8M
    if (arena->reloc_list_head == NULL)
101
1.79k
      arena->reloc_list_head = reloc;
102
103
10.8M
    if (arena->reloc_list_tail != NULL)
104
10.8M
      arena->reloc_list_tail->next = reloc;
105
106
10.8M
    arena->reloc_list_tail = reloc;
107
10.8M
    offset = va_arg(offsets, size_t);
108
10.8M
  }
109
110
3.60M
  return result;
111
3.60M
}
112
113
// Flags for _yr_arena_allocate_memory.
114
2.42M
#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
410M
{
139
410M
  if (buffer_id > arena->num_buffers)
140
0
    return ERROR_INVALID_ARGUMENT;
141
142
410M
  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
410M
  if (b->size - b->used < size)
149
9.39k
  {
150
9.39k
    size_t new_size = (b->size == 0) ? arena->initial_buffer_size : b->size * 2;
151
152
9.39k
    while (new_size < b->used + size) new_size *= 2;
153
154
    // Make sure that buffer size if not larger than 4GB.
155
9.39k
    if (new_size > 1ULL << 32)
156
0
      return ERROR_INSUFFICIENT_MEMORY;
157
158
9.39k
    uint8_t* new_data = yr_realloc(b->data, new_size);
159
160
9.39k
    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
9.39k
#if !defined(_WIN32) && !defined(__CYGWIN__)
167
9.39k
    if (flags & YR_ARENA_ZERO_MEMORY)
168
5.11k
      memset(new_data + b->used, 0, new_size - b->used);
169
9.39k
#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
9.39k
    if (b->data != NULL && b->data != new_data)
174
149
    {
175
149
      YR_RELOC* reloc = arena->reloc_list_head;
176
177
11.6M
      while (reloc != NULL)
178
11.6M
      {
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
11.6M
        uint8_t* base = buffer_id == reloc->buffer_id
184
11.6M
                            ? new_data
185
11.6M
                            : 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
11.6M
        void** reloc_address = (void**) (base + reloc->offset);
190
191
        // reloc_target is the value of the relocatable pointer.
192
11.6M
        void* reloc_target = *reloc_address;
193
194
11.6M
        if ((uint8_t*) reloc_target >= b->data &&
195
11.6M
            (uint8_t*) reloc_target < b->data + b->used)
196
2.24M
        {
197
          // reloc_target points to some data inside the buffer being moved, so
198
          // the pointer needs to be adjusted.
199
2.24M
          *reloc_address = (uint8_t*) reloc_target - b->data + new_data;
200
2.24M
        }
201
202
11.6M
        reloc = reloc->next;
203
11.6M
      }
204
149
    }
205
206
9.39k
    b->size = new_size;
207
9.39k
    b->data = new_data;
208
9.39k
  }
209
210
410M
  if (ref != NULL)
211
410M
  {
212
410M
    ref->buffer_id = buffer_id;
213
410M
    ref->offset = (uint32_t) b->used;
214
410M
  }
215
216
410M
  b->used += size;
217
218
410M
  return ERROR_SUCCESS;
219
410M
}
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
1.79k
{
238
1.79k
  YR_ARENA* new_arena = (YR_ARENA*) yr_calloc(1, sizeof(YR_ARENA));
239
240
1.79k
  if (new_arena == NULL)
241
0
    return ERROR_INSUFFICIENT_MEMORY;
242
243
1.79k
  new_arena->xrefs = 1;
244
1.79k
  new_arena->num_buffers = num_buffers;
245
1.79k
  new_arena->initial_buffer_size = initial_buffer_size;
246
247
1.79k
  *arena = new_arena;
248
249
1.79k
  return ERROR_SUCCESS;
250
1.79k
}
251
252
void yr_arena_acquire(YR_ARENA* arena)
253
113
{
254
113
  arena->xrefs++;
255
113
}
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
1.90k
{
273
1.90k
  arena->xrefs--;
274
275
1.90k
  if (arena->xrefs > 0)
276
113
    return ERROR_SUCCESS;
277
278
23.3k
  for (uint32_t i = 0; i < arena->num_buffers; i++)
279
21.5k
  {
280
21.5k
    if (arena->buffers[i].data != NULL)
281
9.18k
      yr_free(arena->buffers[i].data);
282
21.5k
  }
283
284
1.79k
  YR_RELOC* reloc = arena->reloc_list_head;
285
286
10.8M
  while (reloc != NULL)
287
10.8M
  {
288
10.8M
    YR_RELOC* next = reloc->next;
289
10.8M
    yr_free(reloc);
290
10.8M
    reloc = next;
291
10.8M
  }
292
293
1.79k
  yr_free(arena);
294
295
1.79k
  return ERROR_SUCCESS;
296
1.90k
}
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.75k
{
343
4.75k
  return _yr_arena_allocate_memory(
344
4.75k
      arena, YR_ARENA_ZERO_MEMORY, buffer_id, size, ref);
345
4.75k
}
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
2.41M
{
387
2.41M
  YR_ARENA_REF r;
388
389
2.41M
  int result = _yr_arena_allocate_memory(
390
2.41M
      arena, YR_ARENA_ZERO_MEMORY, buffer_id, size, &r);
391
392
2.41M
  if (result != ERROR_SUCCESS)
393
0
    return result;
394
395
2.41M
  va_list field_offsets;
396
2.41M
  va_start(field_offsets, ref);
397
398
2.41M
  result = _yr_arena_make_ptr_relocatable(
399
2.41M
      arena, buffer_id, r.offset, field_offsets);
400
401
2.41M
  va_end(field_offsets);
402
403
2.41M
  if (result == ERROR_SUCCESS && ref != NULL)
404
2.41M
  {
405
2.41M
    ref->buffer_id = r.buffer_id;
406
2.41M
    ref->offset = r.offset;
407
2.41M
  }
408
409
2.41M
  return result;
410
2.41M
}
411
412
void* yr_arena_get_ptr(
413
    YR_ARENA* arena,
414
    uint32_t buffer_id,
415
    yr_arena_off_t offset)
416
29.3M
{
417
29.3M
  assert(buffer_id < arena->num_buffers);
418
29.3M
  assert(offset <= arena->buffers[buffer_id].used);
419
420
29.3M
  if (arena->buffers[buffer_id].data == NULL)
421
1.95k
    return NULL;
422
423
29.3M
  return arena->buffers[buffer_id].data + offset;
424
29.3M
}
425
426
yr_arena_off_t yr_arena_get_current_offset(YR_ARENA* arena, uint32_t buffer_id)
427
69.6M
{
428
69.6M
  assert(buffer_id < arena->num_buffers);
429
430
69.6M
  return (yr_arena_off_t) arena->buffers[buffer_id].used;
431
69.6M
}
432
433
int yr_arena_ptr_to_ref(YR_ARENA* arena, const void* address, YR_ARENA_REF* ref)
434
8.12k
{
435
8.12k
  *ref = YR_ARENA_NULL_REF;
436
437
8.12k
  if (address == NULL)
438
0
    return 1;
439
440
54.2k
  for (uint32_t i = 0; i < arena->num_buffers; ++i)
441
54.2k
  {
442
    // If the buffer is completetly empty, skip it.
443
54.2k
    if (arena->buffers[i].data == NULL)
444
20.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
33.4k
    if ((uint8_t*) address >= arena->buffers[i].data &&
449
33.4k
        (uint8_t*) address < arena->buffers[i].data + arena->buffers[i].used)
450
8.12k
    {
451
8.12k
      ref->buffer_id = i;
452
8.12k
      ref->offset =
453
8.12k
          (yr_arena_off_t) ((uint8_t*) address - arena->buffers[i].data);
454
455
8.12k
      return 1;
456
8.12k
    }
457
33.4k
  }
458
459
0
  return 0;
460
8.12k
}
461
462
void* yr_arena_ref_to_ptr(YR_ARENA* arena, YR_ARENA_REF* ref)
463
27.4M
{
464
27.4M
  if (YR_ARENA_IS_NULL_REF(*ref))
465
1.16M
    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
26.2M
  return yr_arena_get_ptr(arena, ref->buffer_id, ref->offset);
474
27.4M
}
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
1.19M
{
489
1.19M
  int result;
490
491
1.19M
  va_list offsets;
492
1.19M
  va_start(offsets, buffer_id);
493
494
1.19M
  result = _yr_arena_make_ptr_relocatable(arena, buffer_id, 0, offsets);
495
496
1.19M
  va_end(offsets);
497
498
1.19M
  return result;
499
1.19M
}
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
408M
{
508
408M
  YR_ARENA_REF r;
509
510
  // Allocate space in the buffer.
511
408M
  FAIL_ON_ERROR(_yr_arena_allocate_memory(arena, 0, buffer_id, size, &r));
512
513
  // Copy the data into the allocated space.
514
408M
  memcpy(arena->buffers[buffer_id].data + r.offset, data, size);
515
516
408M
  if (ref != NULL)
517
187M
  {
518
187M
    ref->buffer_id = r.buffer_id;
519
187M
    ref->offset = r.offset;
520
187M
  }
521
522
408M
  return ERROR_SUCCESS;
523
408M
}
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
1.45k
{
531
1.45k
  return yr_arena_write_data(arena, buffer_id, string, strlen(string) + 1, ref);
532
1.45k
}
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
}